Log In  


There was a discussion on twitter about not being able to get the stack trace for a dead coroutine, which is understandably frustrating. However, I was sure that I once figured out a way to do it, and I said so, but the code to do so is on a dead PC at the moment, so I had to spend some time figuring it out again.

This sample basically runs a coroutine that waits for 5 seconds and then does something fatal. Each frame it displays the known status and stack trace. Run it, you'll see.

I tried to set up the code to be as simple and understandable as possible, but if you have questions, please feel free to ask.

EDIT! For a VERY simple coroutine exception stack trace dump, see my follow-up post here

Cart #tehuyajiso-7 | 2019-10-19 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5

5


Hi Felice. Haven't seen you in a-while. Welcome back.

I was looking at your print_wrap function. Wouldn't this be easier ?

function my_wrap(t)
  for i=1,#t,32 do
    ?sub(t,i)
  end
end

If you want a fixed-char length, there is:

function my_wrap(t,w)
  w=w or 32
  for i=1,#t,w do
    ?sub(t,i,i+w-1)
  end
end

Yeah, true. That function was extracted from something a little more monolithic and I never condensed it.

Mind you, you want to go to #str+1, because a print of a blank string is expected to produce a newline, and a string of exactly screen width should cause a newline on the next line, at least according to most consoles.

I changed it.


Interesting discovery, trace().

Not listed in the Pico-8 manual or advanced manual.

Seems to return the line number the code is running at and the function ! Nifty. Seeing what else I can glean from your code.

You were always full of miracles in your coding. Stuff I could learn from. :)

function prinx(t)
  for i=1,#t+1,32 do
    ?sub(t,i)
  end
end

cls()

repeat

pset(rnd(128),rnd(128),rnd(16))
flip()

if btnp(4) then
  prinx(trace())
end

if btnp(5) then
  prinx(trace())
end

until forever

I updated the cart so there's less of me trying to be clever and more of me trying to show the actual API usage. The code inside is simplified.

The only thing that's still me-trying-to-be-clever is the trace prettifier, which I improved. See print_trace() for details, I think it's actually pretty useful for anyone wanting to print a trace that's readable.


Yep, just found out my debugger is expecting a fixed location on where that text appears. Going to have to do something else, like maybe count the apostrophes or something.

Was trying out your print_trace() function, crashed on me:

while e<=#v do
attempt to get length of local 'v' (a nil value).

Copied over the split() function too. Still not working.


1

The print_trace() routine expects you to supply the string returned from a call to trace().

I don't like to couple functionality, so it doesn't fetch the string internally. This way, you can log a trace now and print it later.


Your routine may be more complex than I need, Felice.

I just the need function and line # of where it came from. In many ways you are way ahead of my mode of thinking. :)

. . .

Yep. Got it.

This could've been easier if trace() had used fixed character locations.

-- print function and line #
-- you called this function
-- from.
function debug()
  ...
end

Rest of code plus working example found HERE:
https://www.lexaloffle.com/bbs/?tid=35702


wow I didn't know this was possible. very useful! thanks!


If you mean her, kittenm4ster, yep, she is a bit of a genius you know. I don't understand half of what she's doing or coding. :)

If you mean the debug routine, please post your comment HERE:

https://www.lexaloffle.com/bbs/?tid=35702

Thanks, and will return there tomorrow.


2

Okay, one more follow-up. I just realized that this is probably the method you're intended to use if you just want to stop dead when a coroutine dies:

local running, exception = coresume(co)
if(exception) stop(trace(co, exception))

Seems stop() even wraps the text output properly! 😄

(It's still full of ugly "string:" stuff, but hey, at least it's only a few tokens this way.)

You could even just hook the existing coresume() if you always want your app to die if a coroutine dies:

_real_coresume = coresume
function coresume(co)
  local running, exception = _real_coresume(co)
  if(exception) stop(trace(co, exception))
  return running
end

Felice, is there a way to return a variable name visually ?

a=25
print(debug(a))
b={"1","2","3"}
b[2]=4
print(debug(b))

Output results would be:
a=25
b[3]={"1","4","3"}


Not in Lua, no. The function has no way of knowing the identity of the object you passed, because your name for it will be different from the name inside the function. You pass the object the variable points at, not the variable itself.

You could do it in a language with a preprocessor that allows macros and a stringizing operator, e.g. C/C++:

#define debug2(v) the_real_debug2(v, #v)

Where #v is a preprocessor trick to make <v> into "<v>".

But we don't have such a preprocessor. You'd have to write the second argument by hand for each such call.


Ah, Flipper. That would've been icing on the debugger.

Alright, welp, something to add to the suggestion box. Will post a new entry for this later today.

https://www.lexaloffle.com/bbs/?tid=35330



[Please log in to post a comment]