stacktrace and functions in namespaces
Asked Answered
S

3

10

I am trying to get a backtrace at some point of the execution of my (c++) program.

for that I am using backtrace and backtrace_symbols. Something along this lines:

std::string stacktrace( unsigned int frames_to_skip )
{
    std::string str;

    void* stack_addrs[50];
    int trace_size = backtrace( stack_addrs, 50 );
    char** stack_strings = backtrace_symbols( stack_addrs, trace_size );

    str += "[bt] backtrace:\n";
    // skip frames_to_skip stack frames
    for( int i = frames_to_skip; i < trace_size; ++i )
    {
        char tmp[4096];
        sprintf( tmp, "[bt] #%d %s\n", i-frames_to_skip, stack_strings[i] );
        str += tmp;
    }

    free( stack_strings );

    return str;
}

It works but some functions names are missing. example:

[bt] #0 /path/to/executable() [0x43e1b5]
[bt] #1 /path/to/executable() [0x43e0cd]
[bt] #2 /path/to/executable() [0x43df51]
[bt] #3 /path/to/executable() [0x43dd44]
[bt] #4 /path/to/executable() [0x43db50]
[bt] #5 /path/to/executable() [0x43d847]
[bt] #6 /path/to/executable() [0x43d216]
[bt] #7 /path/to/executable() [0x43c1e1]
[bt] #8 /path/to/executable() [0x43b293]
[bt] #9 /path/to/executable(_Z29SomeRN5other8symbolE+0x2c) [0x43a6ca]
[bt] #10 /path/to/executable(_Z11SomeIN5_8symbolEPFvRS1_EEvRKT_RKT0_+0x77) [0x441716]
...

the functions 0 to 8 have one common point : they all sit in a namespace...
I tried putting function 9 in an anonymous namespace (without any other modification) and it disapears from the backtrace... which now looks like this:

[bt] #0 /path/to/executable() [0x43e1b5]
[bt] #1 /path/to/executable() [0x43e0cd]
[bt] #2 /path/to/executable() [0x43df51]
[bt] #3 /path/to/executable() [0x43dd44]
[bt] #4 /path/to/executable() [0x43db50]
[bt] #5 /path/to/executable() [0x43d847]
[bt] #6 /path/to/executable() [0x43d216]
[bt] #7 /path/to/executable() [0x43c1e1]
[bt] #8 /path/to/executable() [0x43b293]
[bt] #9 /path/to/executable() [0x43a6ca]
[bt] #10 /path/to/executable(_Z11SomeIN5_8symbolEPFvRS1_EEvRKT_RKT0_+0x77) [0x441716]
...

Is there any way to fix that?

p.s.: version of g++: g++ (GCC) 4.6.0 20110530 (Red Hat 4.6.0-9)

edit fixed max depth of the backtrace after Code Monkey remark
edit2 added the full code of the function
edit3 the code is compiled with -O0 -g3 and linked with -rdynamic

Shaduf answered 4/8, 2011 at 20:59 Comment(0)
N
6

Your problem may be the functions you are using. Your max_depth in backtrace(..) is set to 16. That may be too low. At any rate...

This blog post on C++ stack traces with GCC explains how you should be performing stack traces. In sum,

#include <execinfo.h>
void print_trace(FILE *out, const char *file, int line)
{
    const size_t max_depth = 100;
    size_t stack_depth;
    void *stack_addrs[max_depth];
    char **stack_strings;

    stack_depth = backtrace(stack_addrs, max_depth);
    stack_strings = backtrace_symbols(stack_addrs, stack_depth);

    fprintf(out, "Call stack from %s:%d:\n", file, line);

    for (size_t i = 1; i < stack_depth; i++) {
        fprintf(out, "    %s\n", stack_strings[i]);
    }
    free(stack_strings); // malloc()ed by backtrace_symbols
    fflush(out);
}

GCC also provides access to the C++ name (de)mangler. There are some pretty hairy details to learn about memory ownership, and interfacing with the stack trace output requires a bit of string parsing, but it boils down to replacing the above inner loop with this:

#include <cxxabi.h>
...
for (size_t i = 1; i < stack.depth; i++) {
    size_t sz = 200; // just a guess, template names will go much wider
    char *function = static_cast(malloc(sz));
    char *begin = 0, *end = 0;
    // find the parentheses and address offset surrounding the mangled name
    for (char *j = stack.strings[i]; *j; ++j) {
        if (*j == '(') {
            begin = j;
        }
        else if (*j == '+') {
            end = j;
        }
    }
    if (begin && end) {
        *begin++ = '';
        *end = '';
        // found our mangled name, now in [begin, end)

        int status;
        char *ret = abi::__cxa_demangle(begin, function, &sz, &status);
        if (ret) {
            // return value may be a realloc() of the input
            function = ret;
        }
        else {
            // demangling failed, just pretend it's a C function with no args
            std::strncpy(function, begin, sz);
            std::strncat(function, "()", sz);
            function[sz-1] = '';
        }
        fprintf(out, "    %s:%s\n", stack.strings[i], function);
    }
    else
    {
        // didn't find the mangled name, just print the whole line
        fprintf(out, "    %s\n", stack.strings[i]);
    }
    free(function);
}

There is more information on that site (I didn't want to copy verbatim) but looking at this code and the above site should get you on the right track.

Novitiate answered 4/8, 2011 at 21:7 Comment(1)
hello, thanks for your answer. you're right about the max depth, the end of the backtrace was missing, but that's not a problem since those missing calls were from the test framework I'm using. Still, the problem remains and I'm doing the same as the blog post says. One interesting fact however, the test framework does have functions in namespaces and they are visible in the stacktrace, my functions are not ... I did the test again (putting a function in a namespace), and the same hapenned again (the function is no longer visible in the backtrace...Shaduf
L
1

Can you try adding -rdynamic to your link?

http://www.linuxforums.org/forum/programming-scripting/35192-backtrace_symbols-no-symbols.html

Leopoldine answered 4/8, 2011 at 21:42 Comment(0)
M
1

backtrace lists the call frames, which correspond to machine code call instructions, not source level function calls.

The difference is that with inlining, an optimizing compiler can often avoid using a call instruction for every logical function call in the source code.

Maleki answered 5/8, 2011 at 0:40 Comment(1)
In that case they would not appear at all in the bt. Anyway there are no optimisations here, it's compiled with -O0 -g3. Moreover if I break in the function which construct the backtrace, and go to the address under one with no symbol, in the disassembly view (I'm using eclipse), I end up right after a call instruction wich is correctly displayed (aka shows the name of the function/method it's going to jump into).Shaduf

© 2022 - 2024 — McMap. All rights reserved.