Infinity and Beyond
RSS icon Email icon Home icon
  • Runtime backtrace in C++ with name demangling

    Posted on September 10th, 2009 Mike 11 comments

    I needed to hack together a quick function to do a backtrace in C++. The backtrace() functionality is well documented on the ‘net, however demangling C++ symbols isn’t. Here’s what I did:

    First, call backtrace. I’m sure you can write your own cleaner version:

    #define MAX_FRAMES 100
    void mytrace() {
     void* addresses[MAX_FRAMES];
     int size;
     size = backtrace(addresses, MAX_FRAMES);
     char** symbols = backtrace_symbols(addresses, size);
     int x;
     for (x = 0; x < size; ++x) {
     printf("%s\n", demangle(symbols[x]).c_str());
     }
     free(symbols);
    }

    As you can see, I’ve run my backtrace string through a demangle() function. The important parts of demangle() are sscanf() and abi::__cxa_demangle(). In sscanf(), we take the backtrace()-generated symbol (which looks something like “./testlog(_ZN7Logging7mytraceEv+0x1f) [0x401b77]“), and pickout the C++ symbol _ZN7Logging7mytraceEv. The __cxa_demangle() function is able to demangle this string into something that makes sense to the user (Logging::mytrace() in this example).

    std::string
    demangle(const char* symbol) {
      size_t size;
      int status;
      char temp[128];
      char* demangled;
      //first, try to demangle a c++ name
      if (1 == sscanf(symbol, "%*[^(]%*[^_]%127[^)+]", temp)) {
        if (NULL != (demangled = abi::__cxa_demangle(temp, NULL, &size, &status))) {
          std::string result(demangled);
          free(demangled);
          return result;
        }
      }
      //if that didn't work, try to get a regular c symbol
      if (1 == sscanf(symbol, "%127s", temp)) {
        return temp;
      }
     
      //if all else fails, just return the symbol
      return symbol;
    }

    I’m sure the rest of you can come up with something cleaner, but this worked for me.  Also, don’t forget to compile your code with ‘-rdynamic’.  It tells the linker to use ‘-export-dynamic’ which puts ALL symbols into the symbol table.  This is primarily needed for the dynamic linker functions (dlopen, dlsym, etc), but also benefits runtime backtraces.

     

    10 responses to “Runtime backtrace in C++ with name demangling” RSS icon

    • Great, that saves my day. But there’s a mistake in demangle: you should put a “static” before “char temp[128]“, otherwise you return an address to a memory object, which does not exist anymore when you return from demangle. That is undefined behaviour.

    • Glad I could help someone!
      I see what you’re saying about the static buffer. I figured it would be ok since I was returning a ‘std::string’ and the local buffer would be used to initialize a string object. I guess if the string object was constructed outside the scope of this function, that could definitely be a problem. I’ll see if I can do some research on that one. FWIW, I have this snippet of code running in a logging framework and haven’t seen a problem yet.

    • Ah, I see, you return a std::string. Sorry, my fault, for my need I changed that to “const char *” (I need more coffee), and this case it was definitely a problem. In the case of std::string I’m as well not sure what really happens.
      But you’re right, in most cases it should be no problem.

    • There’s a memory leak in demangle(), You should free the result of abi::__cxa_demangle before returning, that is:

      if (NULL != (result = abi::__cxa_demangle(temp, NULL, &size, &status))) {
      string resultStr(result);
      free(result);
      return resultStr;
      }

    • Oh, and the first scanf should take the width of length in account. That is

      sscanf(symbol, “%*[^'(']%*[^'_']%127[^')''+']“, temp)

      instead of just

      sscanf(symbol, “%*[^'(']%*[^'_']%[^')''+']“, temp)

    • @Fons – You are absolutely correct. I fixed the code. Thanks!

    • Two more remarks:

      1. the call to sscanf() is not totally right yet. Although they are superflous herem the quote characters (‘) should not be used since they are interpreted just like any other character.

      sscanf(symbol, “%*[^'(']%*[^'_']%127[^')''+']“, temp)

      should really be

      sscanf(symbol, “%*[^(]%*[^_]%127[^)+]“, temp)

      2. It would be worth mentioning that backtrace_symbols() is not always useful without compiling with -rdynamic

    • @Fons – Done. Thanks again!

    • great help mate – worked like a charm. one remark though.

      0. in order to be able to use abi::__cxa_demangle() you need to include

      #include

      cheers;

    • great help mate – worked like a charm. one remark though.

      0. in order to be able to use abi::__cxa_demangle() you need to include “cxxabi.h”

      cheers;

      ps: previous submit did not show the name of the header somehow…


    1 Trackbacks / Pingbacks

    Leave a reply