There are a few utilities in the C++ code that can be used to make debugging easier. Since Storm generates code, it is harder to use some functionality in C++ debuggers as usual. For example, stack traces from within debuggers are unaware of the function names for generated code. Therefore, it is not possible to step through Storm code in source mode, or set breakpoints in Storm code easily.
storm::dbgBreak() is useful for breaking the program at an arbitrary point. It is
exposed to Storm as
core.debug.dbgBreak(), which makes it useful in Storm code as well. On
Windows, it calls the function
DebugBreak(), which breaks into a debugger if you have one
installed. On Linux, it raises the signal SIGINT, which is easily seen inside GDB.
To print a stack trace, call the function
storm::stackTrace() from C++ or
core.debug.stackTrace(). This stack trace will properly print the names of functions inside Storm
in addition to the functions from C++. If you are writing code in a shared library,
storm::stackTrace is not accessible, in which case one can use
Signals on Linux
There are a few additional things one need to consider when debugging Storm on Linux. Since the garbage collector uses signals to synchronize scanning of the thread's stacks (SIGXFSZ and SIGXCPU), GDB will halt the program execution when that happens. Furthermore, the garbage collector utilizes memory protection features in Linux to detect when data has been read or written, which can be used to avoid doing unnecessary work. The downside of this is that SIGSEGV signals are generated and handled during normal program operation. Thus, SIGSEGV can not be used to find memory errors. To simplify debugging, Storm installs another signal handler for SIGSEGV that is executed if the garbage collector determined that the SIGSEGV represented a genuine segmentation fault. This handler simply raises SIGINT so that it is easy to distinguish between segmentation faults and normal GC operation. However, for various reasons, the final SIGINT may be handled by a thread different from the thread where the segmentation fault occurred.
To make GDB ignore the signals used with the garbage collector, put the following in your
file, or type them into the interactive prompt when you start GDB:
handle SIGXFSZ nostop noprint handle SIGXCPU nostop noprint handle SIGSEGV nostop noprint
If you are developing languages or language extensions, it can sometimes be convenient to see the final machine code that is generated instead of the intermediate representation. This can be done by the following C++ code:
// See the transformed machine code (l is a Listing): PLN(engine().arena()->transform(l, null)); // See the binary representation as well: PLN(new (engine()) Binary(engine().arena(), l, true));