You are currently browsing the monthly archive for August 2008.

I recently stumbled across an article referencing macros defined by gcc. That list is pretty daunting! If you compare it to GCC Common Predefined Macros (the official source), you realize quite fast that a lot of those macros exist for the benefit of libc and libstdc++ library authors—not compiler end users such as myself.

For whatever reason, it takes quite a bit of Google-Fu (or luck) to get GCC’s official page to show up on the first page of search results. Whenever I search for it, if I don’t remember the page title exactly, I have to dig through about 20 other sites before finding it. In any case, both of those lists are frigging huge, so here’s a bit of a smaller list that I usually have up my sleeves:

CHAR_BIT
Use this instead of __CHAR_BIT__. Required by the C and C++ language standards (IIRC) and defined in <limits.h> or <climits>. Number of bits in a byte. I’m not old enough to have worked on any machines that didn’t have 8-bit bytes, but they certainly existed at one point (and it’s the reason why my university networking class used the term octets instead of bytes).
__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
GCC-specific versioning info, considered as a tuple. For example, if I’m using GCC 4.3.1, those values will respectively be (4, 3, 1). Simple, and useful for code that needs to be compatible across multiple versions of the compiler, or requires language features that are newer (such as using GCC’s new builtin atomic intrinsics).

If you want to simply identify any GCC-family compiler (including the Intel C/C++ compilers), simply look for __GNUC__‘s existence.

size_t, ptrdiff_t
Use these instead of __SIZE_TYPE__, __PTRDIFF_TYPE__. Required by the C++ language standard and defined in <stddef.h> or <cstddef>. Occasionally, I’ll write code that should follow STL semantics, but it doesn’t bring in any other parts of the STL. I always forget that <cstddef> is the header to bring in for the machine-agnostic size types.

NULL
Still not quite as good as nullptr, GCC does have some magic that provides better type-checking than just using the literal 0 in C++. However, I’ve recently found that in GCC 4.3.0 that it introduces ambiguouity: std::vector<T*>::push_back(NULL) confuses the compiler, and I have to instead write T *nullptr = NULL; std::vector<T*>::push_back(nullptr). Once that breaks, I’ll just delete the first line.
__STRICT_ANSI__
Defined by GCC if the -ansi flag is set. You probably don’t need to depend upon it, but if you really want to be anal (or a jerk, depending on your point of view), you could error out if __STRICT_ANSI__ was not set, thereby forcing users to crank up the standardization in their compilers. In my experience, this in combination with Visual Studio’s DisableLanguageExtensions setting makes it much easier to write code that works across both GCC/Linux and MSVC/Win32.
__TIMESTAMP__, __BASE_FILE__
Ever need to get automated build metrics out of a compilation phase? __TIMESTAMP__ records the system time when the compile took place, and __BASE_FILE__ records the name of the source file being compiled (as opposed to __FILE__, which lists the current file). You could craft up a common project header file that somehome embedded these values into the object file (say by using a static global in an anonymous namespace that writes those strings to an empty file descriptor). Not useful in 99% of the cases, but it’s a good way to add forensic traceability data to a build.
__FILE__, __LINE__, __func__, __PRETTY_FUNCTION__
Diagnostics for “where am I?” Note that the first three are standard and provided by any compiler, __PRETTY_FUNCTION__ is a GCC-ism and contains the “decorated name”, which includes all of the arguments and template instantiations. The only good reason I’ve heard to use __PRETTY_FUNCTION__ is the template instantiation inclusion (e.g. “this problem happens with std::vector<MyCrappyType>, and not with just any vector type).
__COUNTER__
This macro is also provided by the Visual Studio compiler. It simply expands to an increasing integer value, and the only use I’ve found for it is to provide unique ids in auto-generated code (say, for the TUT C++ testing framework).
__cplusplus
A necessity for writing headers compatible with both C and C++. ‘Nuff said.
linux, unix, i386
Since I don’t really write code that’s cross-UNIXish-platform, I’ve never really had to mess with GCC on other platforms (BSD/Solaris/AIX/Win32), nor have I had to use other non-GCC-family compilers on Linux. I imagine these would come in handy for those cases, but I can’t speak to their exact uses.

Since I just noticed the original article I referred to has a snide comment about development environments, I’ll include this just for kicks: Predefined Macros in VC++ 2005.

Advertisements

First, a word of warning: This is not portable. Secondly… being able to produce stack traces (outside of the debugger) is something that’s usually reserved for languages like Python or Java… but it’s quite nice to have them in C++. There’s several hurdles to overcome, however.

Acquire Stack

This part is pretty easy, but unless you’re nosy with the header files in /usr/include, it’s not likely to stumble upon this by chance.

#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);
}

Demangle C++ Names

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. You could do a bit more optimization, but I’ll leave that as an exercise to the reader. The important thing is to obey exactly what the ABI requires regarding dynamic memory:

  • you pass me a buffer created by malloc, along with the current size of the buffer.
  • i might realloc your buffer to make space for the whole name, and I’ll return the result, which may be different. Or I’ll fail and return NULL, because you didn’t pass in a mangled name I could understand.
  • you free my return value when you’re done (unless I returned NULL).

Otherwise, you’ll get a segmentation fault somewhere along the line, and a stack trace that blows up the program isn’t useful except for post-mortem analysis in gdb.

Export Symbols

Even if you go through all of the other steps, if you don’t account for this in your build phase, you will get a pretty useless stack trace. Here’s what I got from my experiment initially:

Call stack from backtrace.cxx:105:
    debug/backtrace:__gxx_personality_v0()
    debug/backtrace:__gxx_personality_v0()
    debug/backtrace:__gxx_personality_v0()
    debug/backtrace:__gxx_personality_v0()
    /lib/tls/i686/cmov/libc.so.6:__libc_start_main()
    debug/backtrace:__gxx_personality_v0()

After poking around, I realized I had to add -rdynamic to my linker flags so that all symbols would be exported into the executable. I haven’t experimented, but I would guess this applies to shared-object building as well.

With -rdynamic, my stack trace looks a lot nicer:

Call stack from backtrace.cxx:105:
    debug/backtrace:hot_potato::pass(double, double)
    debug/backtrace:hot_potato::pass(int)
    debug/backtrace:hot_potato::pass()
    debug/backtrace:main()
    /lib/tls/i686/cmov/libc.so.6:__libc_start_main()
    debug/backtrace:__gxx_personality_v0()

Bingo! I only get a source file and line number from the call site, but it’s not too difficult to trace back through callers from this point. In this case, my executable is named debug/backtrace and hot_potato is just an example class that calls its own functions to give me a pretty chain to look at.

Usage

Being able to create a stack trace is only half of it. Now you actually have to use it to get any value out of it. Here’s the rub: This is most useful when an exception gets caught, but the data is already gone by that time, because the exception’s been thrown. Logically then, it seems to make sense to encapsulate this functionality into an exception class (e.g. class app_exception : public std::exception) that gets thrown. Simply replace all of the fprintf calls with something that generates a string. execute it in the exception class’s constructor, and print it out in the catch block.

Another option would be to allocate thread-local storage for stack traces (similar to the current thread-local errno), and then have all of your important functions call set_thread_stacktrace, which populates that thread-local storage. Then the exception handlers can just pull that data regardless of the type of exception thrown. I think this is better from the flexibility aspect, but I haven’t actually investigated the feasability of it nor the performance impact of recalculating this all the time.