Sunday, October 26, 2008

c/c++: call stack

A lot of languages such as python, java, ruby etc. produce call trace on exception.

In c/c++ you don't have such opportunity.
Only playing with a debugger it's possible to view the call trace.

In glibc(since 2.1) you can find backtrace function defined in execinfo.h
This function returns a backtrace for the calling program(addresses of the functions).
Using backtrace function with backtrace_symbols you can get symbol names.
backtrace_symbols returns the symbolic representation of each address consists of the function
name (if this can be determined), a hexadecimal offset into the function, and the actual return address (in hexadecimal) according to the man pages. It should look like:

message: ./a.out(_Z1cv+0x19) [0x80488ad]
message: ./a.out(_Z1bv+0xb) [0x804896f]
message: ./a.out(_Z1av+0xb) [0x804897c]
message: ./a.out(main+0x16) [0x8048994]
message: /lib/libc.so.6(__libc_start_main+0xfb) [0xb7e2764b]
In c++, due to function overloading, namespaces, etc., names of the functions are being mangled. It's hard to read this traceback. I really didn't want to parse each string to extract function names but tried to use dladdr, glibc extension for dlfcn. dladdr returns function name and the pathname of shared object that contains this function. Unfortunately dladdr still returns mangled name of the function.
Unexpectedly I have found __cxa_demangle hidden in the caves of g++. This function demangles function name. This is what I was looking for.
The short example of traceback generation:
#include <stdio.h>
#include <signal.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <dlfcn.h>
#include <stdlib.h>

void c()
{
    using namespace abi;

    enum
    {  
        MAX_DEPTH = 10 
    }; 

    void *trace[MAX_DEPTH];

    Dl_info dlinfo;

    int status;
    const char *symname;
    char *demangled;

    int trace_size = backtrace(trace, MAX_DEPTH);

    printf("Call stack: \n");

    for (int i=0; i<trace_size; ++i)
    {  
        if(!dladdr(trace[i], &dlinfo))
            continue;

        symname = dlinfo.dli_sname;

        demangled = __cxa_demangle(symname, NULL, 0, &status);
        if(status == 0 && demangled)
            symname = demangled;

        printf("object: %s, function: %s\n", dlinfo.dli_fname, symname);

        if (demangled)
            free(demangled);
    }  
}

void b()
{
    c();
}

void a()
{
    b();
}

int main(int argc, char **argv)
{
    a();

    return 0;
}
The executable should be compiled with -rdynamic g++ flag to instruct the linker to add all symbols, not only used ones, to the dynamic symbol table.
g++ test.cc -ldl -rdynamic -o test
The output:
Call stack: 
object: ./test, function: c()
object: ./test, function: b()
object: ./test, function: a()
object: ./test, function: main
object: /lib/libc.so.6, function: __libc_start_main
If gcc optimization have been used you may not see the whole traceback in some cases. With -O2/-O3
Call stack: 
object: ./test, function: c()
object: ./test, function: main
object: /lib/libc.so.6, function: __libc_start_main

5 comments:

Unknown said...

The code worked on g++ 3.4.6 in 1 go :)
i used to invoke "/usr/bin/pstack" from my program to get the stack trace but obviously that is too slow if no of invocations are high.
Your "demangled" backtrace utility was a great help.

thanks
mux

MartinB said...

Thanks a lot, that was incredibly useful and very clear as well.

Ni@m said...

I'm glad that it was helpful! =)

szs said...

Nice solution. I am trying to get the file and line of the call of a function in the call stack. Everything I tried resulted in the line of the return address. This can be the line after the call or the line of a loop condition, which can be quiet confusing.

Any ideas on how to get that information?

Ni@m said...

Well, you can do that by parsing DWARF sections of the ELF. Anyway it will not give you precise line in the code especially if you build with -O1 or higher.