IMVU's CallStack API Now Open Source!
I'm proud to announce that IMVU has open-sourced its C++ CallStack API! It's available under the MIT license at our SourceForge project. You can view the code here.
CallStack is a simple API for recording and displaying C++ call stacks on 32-bit Windows. To display the call stack at the current location:
printf("%s\n", CallStack::here().asString().c_str());
To grab a CallStack from an arbitrary thread:
HANDLE other_thread_handle = ...; CallStack other_thread(other_thread_handle);
From a structured exception:
Context ctx; CallStack cs; __try { // broken code } __except ( ctx = *(GetExceptionInformation())->ContextRecord), cs.getFromContext(ctx), EXCEPTION_EXECUTE_HANDLER ) { // display cs.asString() }
At first, the format of CallStack.asString()
is a bit confusing, but with your symbol server it contains everything necessary to generate a symbolic call stack, including file names and line numbers.
Here is an example CallStack.asString()
result:
PYTHON25.DLL#b57f5c3ff1b64eda861d97643831ce701!000266dc boost_python.dll#507f2f0a5fd34e65af25e728d0be9ebb1!0000d4bf _avatarwindow.pyd#5289bbd0ff9c4ceab5198308f99ef9271!0002f76a
The lines are formatted module_name#module_hash!offset
. module_name
is the name of the DLL or EXE in which the function lives. module_hash
is a unique hash that identifies a build of a particular module. offset
is the offset of the line of code in bytes from the start of the module. With this information, you can look up a function name and line number for each entry in a call stack.
Fortunately, we have a tool that automates this process: symbol_dump.py! Running it with the previous call stack on the clipboard produces this output:
PYTHON25.DLL#b57f5c3ff1b64eda861d97643831ce701!000266dc ...t\python-2.5.1-src\objects\abstract.c (1860): PyObject_Call boost_python.dll#507f2f0a5fd34e65af25e728d0be9ebb1!0000d4bf ...0\libs\python\src\object\function.cpp ( 614): function_call _avatarwindow.pyd#5289bbd0ff9c4ceab5198308f99ef9271!0002f76a ...\boost\function\function_template.hpp ( 132): boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool,boost::python::detail::translate_exception<IMVUError,void (__cdecl*)(IMVUError const &)>,boost::_bi::list3<boost::arg<1>,boost::arg<2>,boost::_bi::value<void (__cdecl*)(IMVUError const
That last function name is pretty epic (as are most Boost or C++ function names), but notice that the call stack has accurate file names and line numbers.
The astute reader might ask "Don't minidumps contain stack traces too?" The answer is yes, but minidumps are often inconvenient. Consider the common case:
- Open crash report
- Download mini.dmp to the desktop
- Open mini.dmp in Visual Studio
- Press F11
- Open the call stack debug window if it's not open
With CallStack, we can shorten that to
- Open crash report
- Copy the call stack
- Run symbol_dump.py
Also, for reasons I don't understand, sometimes Visual Studio fails to produce an informative stack when CallStack succeeds.
CallStack is a handy tool for debugging crashes from the wild, and I'm happy that we were able to make it available.
See discussion on Hacker News (2009).
Cool! I ended up implementing almost the exact same thing for DoD... but it would just try to translate the symbols on the spot (which meant our dev team needed .pdb files around). Translating the addresses later would make the tool a bit more useful, good idea!
Great. :) It turns out that almost everybody reimplements this stuff. Google has their breakpad API, but it's not a general solution. Maybe we should fold CallStack into breakpad?