No navigation frame on the left?  Click here.

StackWalk

 

[2 Jan 00] It was pointed out to me that the stack base need not be initialized before calling StackWalk() the first time (and if one does so, surely the stack base should go there, not the current frame pointer). I changed the sample to reflect this; it now initialises only STACKFRAME.AddrPC and STACKFRAME.AddrFrame, neither of which can be omitted.

[20 Dec 99] I have had reports that the SymGetLineFromAddr() invocation refuses to do its job. This is most likely because you are using the NT4 version of imagehlp.dll and dbghelp.dll. The Platform SDK documentation says that SymGetLineFromAddr() is only available with NT5, but it also says that the NT5 versions of imagehlp.dll and dbghelp.dll run on NT4 and are redistributable.

[21 Sep 99] The stackwalk sample got a facelift on 21 Sep 1999 -- see below! Are you using Win9x? I am not, and I would appreciate if you could compile and run this sample and let me know whether it worked.

Stackwalk.zip contains a small but complete project created with Visual C++ 6.0 which demonstrates the use of IMAGEHLP.DLL to create a stack trace, complete with symbols, line numbers, and whatnot. Note that you cannot dump the current thread's stack -- you must spin off a worker to do that job for you.

Well, actually you can dump the current thread's stack, if it is frozen in a suitable state. A suitable state would be while in the filter function for an exception handler, for example, and the update (uploaded on 27 Dec 98) demonstrates this.

Here is how it works, for Structured Exception Handling: You need to call GetExceptionInformation() in the filter expression of your __except handler. Usually, you would use something like

    __except ( FilterFunction( GetExceptionInformation() ) )

While in there, you can dump your stack with impunity. You can also save the entire CONTEXT record (pointed to by a member of the EXCEPTION_POINTERS that GEI() returns) and do the dump later, but then you run a high risk of having your stack unwound before you get around to dumping it (namely, if the exception occured in a function called from the frame handling it).

For C++ exception handling, I recommend you install a translator function -- look up _set_se_translator(). From within your translator function, you can use the supplied EXCEPTION_POINTERS to do your stack dump. If you save the information for later use, you may run into the problem described above.

Finally, a hint if you use VC++ 6.0 and exception handling. For VC++, a "synchronous" exception is one raised by a "throw" statement; everything else is an asynchronous exception (i.e., one which the compiler cannot anticipate). If you think your code may throw Structured Exceptions -- even if you have a translator function! -- you must enable support for asynchronous exceptions. To that end, I strongly recommend unchecking the "enable exception handling" checkbox in the C++ page of the Project Options dialog and typing the option "/EHsa" into the options text box. (The checkbox enabling exception handling sets the equivalent of /EHcs -- no asynchronous exception support, and any 'extern "C"'-funtion is assumed to never throw an exception. Now there is what I call an optimistic assumption.)

21 Sep 1999: A sorely needed update.

The sample was hitherto restricted to NT, as it expected SymInitialize() to retrieve the module list and load the symbols -- which doesn't work on Win9x. In the past days, I had at least three requests to fix this. Now, the search path is constructed by hand (just to show how) and passed to SymInitialize(); then, enumAndLoadModuleSymbols() is called. That function calls fillModuleList() and then iterates through the list and loads whatever it can in the way of symbols.

fillModuleList(), in turn, first calls fillModuleListTH32() which attempts to use Toolhelp32 to do its thing; this should work on Win9x and NT5. If it fails, fillModuleList() assumes that it runs on NT4, or that someone is messing with it, and falls back on fillModuleListPSAPI(), and you can already guess how that works. Toolhelp32 and PSAPI functions are accessed through LoadLibrary() and GetProcAddress(), for obvious reasons.

If you have downloaded previous versions of this sample, you will note that the zip file has grown from 6K to 64K. I would like to ascribe this to my programming prowess and unparalleled productivity, but the truth of the matter is that I included (in the dbg and relsym directories) compiled executables -- both for instant gratification and so you see what kind of information you can get from a mere stack dump. (Next on the list: local variables. This will take a while, though.)

Also, the project file demonstrates the settings for a release build with symbols in COFF format, packaged into a single executable. Who says that we cannot get useful data from a crash at a customer site?

stackwalk.zip, 64 KB