mr-edd.co.uk :: horsing around with the C++ programming language

More stack trace code for Mac OS X and Windows

[9th October 2007]

There have been a fair number of people visiting this site looking for stack trace code recently. I've collected some more since my previous post. I was planning to release it as part of a general debugging library, but I feel cruel holding it back when so many people would find it useful.

So attached to this post is I've made available a small library (one source file and one header) to obtain a stack trace through a portable interface. The code works with the following compilers/systems:

I suspect other versions of Visual C++ will work just fine, too.

The library provides an interface to iterate over the call stack. You can look in the header for more details, but here's a quick example:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <dbg/stack.hpp>

void e()
{
    dbg::stack s;
    std::copy(s.begin(), s.end(), std::ostream_iterator<dbg::stack_frame>(std::cout, "\n"));
}

void d() { e(); }
void c() { d(); }
void b() { c(); }
void a() { b(); }

int main()
{
    a();
    return 0;
}

Output such as the following is produced, showing the instruction pointer, the name of the function, and the module in which the function resides:

0x40144f: e() in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x40163f: d() in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x40164d: c() in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x40165b: b() in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x401669: a() in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x4016c8: main in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x40124b: [unknown function] in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x401298: [unknown function] in P:\software\dbg\src\examples\print_stack\obj\mingw\debug\example.exe
0x7c816fd7: RegisterWaitForInputIdle in C:\WINDOWS\system32\kernel32.dll

Things to be aware of

When using the library on a Mac, you shouldn't need to link against any other libraries. Just compile and go.

When compiling with Microsoft Visual C++, you need to link against imagehlp.lib.

When compiling with MinGW, you need to make sure that your code is compiled with -gstabs (-ggdb won't cut it). You'll also need to link against libbfd, libiberty, libpsapi and libimagehlp.

Note that while I'm releasing this under the boost software license as usual, libiberty and libbfd are libbfd is licensed under the GPL. This means any software that you distribute containing this library will have to be licensed under the GPL. But you are free to remove the MinGW-specific stuff and then license your software pretty much however you want.

I'm still lacking code that works on Linux (looking for x86, primarily). Even the backtrace stuff in glibc doesn't work for me on Ubuntu 7.04. If anyone has any ideas, please let me know!

Head over to the project page for the download.

Comments

ury

[11/10/2007 at 07:05:00]

hi,

why not to use backtrace and backtrace_symbols if GCC ?

Edd

[11/10/2007 at 16:46:00]

Hi ury,

Unfortunately backtrace and backtrace_symbols aren't as ubiquitous as one might imagine. MinGW doesn't provide it and it isn't available on the Mac.

Also, as I noted in the post, I couldn't even get it to work on Linux. Perhaps C++ name mangling confuses it? I need to look in to that some more...

Lastly, I'm just not a fan of the GPL (the shear complexity of it bothers me more than anything else) so I like to keep GPL licensed code out as best as I can. That said, if backtrace did work for me, I would probably add it as an optional extra since the MinGW implementation relies on GPL licensed libs, anyway.

But hey, if backtrace works for you, it should be pretty trivial to add yourself.

Edd

AYF

[04/03/2008 at 21:37:00]

Just as a note, for a Unicode build, the winapi functions GetModuleFileNameA GetModuleFileNameExA LoadLibraryA are compatible replacements for the (const char*) return of std::string::c_str(). Otherwise, they expect a (const wchar_t*).

AYF

[10/03/2008 at 01:52:00]

Thanks for the great code. I'd like to point out, that making "symbol_context sc"in 'fill_frames' static, improves performance drastically ( and it makes sense: why 'Initialize symbol handler for [ the ] process' over and over? ). This comes in handy for me, because I dumped the stack every time my app entered a critical_section in my code ( to track down a deadlock ), and it froze up. 'fill_frames' is anyway protected by a critical_section, so many of its members can be made static, such as 'windows_dll kernel32' ( why load the dll each time? ), etc.

DarkAngel

[10/04/2008 at 03:31:00]

Thanks for the really useful code Edd!

"Note that while I’m releasing this under the boost software license as usual, libiberty and libbfd are licensed under the GPL."

I just want to point out that libiberty is licensed under the LGPL not the GPL, which is much less restrictive (i.e. usable by almost everyone).

It would be nice if this didn't rely on libbfd so those of us untainted by complex and restrictive licenses could use it ;)

Edd

[13/04/2008 at 13:55:00]

Quite right, thanks! I'll update the text correspondingly.

I've heard (from AYF via email) that you can do some magic with the Wine DLLs to remove the need for libiberty and libbfd completely, but it's not something that I've tried.

Apparently, you can use SymGetSymFromAddr() from Wine's dbghelp.dll, though you have to use the native dbghelp.dll for the other functions (you must explicitly load each function used from the appropriate version of the DLL!).

Romko Goofique

[22/05/2008 at 13:40:00]

Edd thanx for the code (i was just struggling with handling backtrace on mac)
Also, as I noted in the post, I couldn't even get it to work on Linux. Perhaps C++ name mangling confuses it?
you can use

#include // cxxabi.h - low-level c++-specific routines

.......
void** bt = new void*[STACK_LENGTH];
size_t btSize = backtrace(bt, STACK_LENGTH);
char** strings = new char*[btSize];
strings = backtrace_symbols(bt, btSize);

for (uint i = 0; i < btSize; i++){
// strings[i] - every stacktrace line
//
// // use abi::__cxa_demangle, to see the human-readable name of methods
// char* demangled = abi::__cxa_demangle(mangled.toLatin1(), NULL, 0, &status);
}
.......

hope that helps

(optional)
(optional)
(required, hint)

Links can be added like [this one -> http://www.mr-edd.co.uk], to my homepage.
Phrases and blocks of code can be enclosed in {{{triple braces}}}.
Any HTML markup will be escaped.