You are hereBlogs / karltk's blog / Stack tracing improvements
Stack tracing improvements
A limitation of my previous stack tracing patches was that io-wrap and io-stream-wrap did not properly report traces on failure. The reason for this is easy to spot if we look at how the error is handled (this is where execution flow ends up when you call io-wrap):
option-wrap(opts, usage, about, announce, s) =
parse-options(opts, usage, about)
; announce
; (s; report-success <+ report-failure)
report-failure =
report-run-time
; <fprintnl> (stderr(), [ (), ": rewriting failed"])
; <exit> 1
As you can imagine, even though the program now happily prints a stack trace when the main strategy exits with a failure, it will not be printed when exit is called.
I've introduced a couple of stack introspection functions for dealing with this: stacktrace-get-current-frame-name returns the name of the current frame s, stacktrace-get-all-frame-names returns a list of all frame names and, stacktrace-get-current-frame-index returns integer that holds the current depth of the stack. These are actually implemented by primitives in the Stratego Standard Library (SSL).
A caveat of these strategies is that calling them will of course alter the stack. Even in the wonderful world of computing, we're not entirely free of Heisenbergian effects, apparently. However, there's a simple workaround: call the primitives directly, since this bypasses the way the compiler registers the stack frames.
With this trick in hand, I rewrote the two above strategies to include proper stack tracing for io-wrap:
option-wrap(opts, usage, about, announce, s) =
parse-options(opts, usage, about)
; announce
; (s; report-success <+ prim("SSL_stacktrace_get_all_frame_names") ; report-failure)
report-failure =
?stacktrace
; report-run-time
; <fprintnl> (stderr(), [ <whoami> (), ": rewriting failed, trace:"])
; <reverse ; map(<fprintnl> (stderr(), ["\t", <id>]))> stacktrace
; <exit> 1
Applying the modified io-wrap on the following sample program
main = io-wrap(my-wrap(foo)) my-wrap(s) = s foo = debug(!"foo") ; bar bar = debug(!"bar") ; fap ; zap fap = debug(!"fap") ; id zap = debug(!"zap") ; debug ; fail
gives
./prog: rewriting failed, trace:
main_0_0
io_wrap_1_0
option_wrap_5_0
lifted144
input_1_0
lifted145
output_1_0
lifted0
my_wrap_1_0
foo_0_0
bar_0_0
zap_0_0
Due to the compiler lifting inner strategies into freshly named, top-level strategies, the trace will contain some lifted* entries. Also, should you call strategies or rules which are compiled with older versions of the compiler, there will be "dark spots" in your trace. It won't be truncated -- only the frames due to the old library will be hidden.
- karltk's blog
- Login or register to post comments