C++ Stacktraces

Tue 12 June 2018


Debugging C++ can be a bit cumbersome. Including the symbol table along with your binary increases it's size, but doesn't hurt the performance. There is addr2line that convert addresses into file names and line numbers but that's an extra step.

The basic solution is to install a SIGSEGV handler.

When deploying an application Google's breakpad is probably the best solution though I found it a bit cumbersome for local development.

For local development backward handles segfaults and exceptions really well and converts memory addresses to line numbers automatically, plus even prints out a few lines of context of code around the stack trace.

I also tried was DeathHandler.

#include "death_handler.h"

void baz() {
 int *foo = (int*)-1; // make a bad pointer
  printf("%d\n", *foo); // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }
int main(int argc, char **argv) {
  Debug::DeathHandler dh;
  foo();
  return 0;
}

In WORKSPACE

new_git_repository(
    name = "death_handler",
    remote = "https://github.com/vmarkovtsev/DeathHandler",
    commit = "6b8599b81d54734e42f09c146cbde1049f1e8b69",
    build_file_content = """
cc_library(
    name = "death_handler",
    visibility = ["//visibility:public"],
    srcs = [
        "death_handler.cc",
    ],
    hdrs = ["death_handler.h"],
    linkopts = ["-ldl"],
)
"""
)

Compile a binary with -g and you can still use -c opt though it will only give you the line of the segfault and not the entire stacktrace.

bazel run -c opt --copt=-g :segfault_binary
zsh: suspended (signal) bazel run -c opt --copt=-g :segfault_binary
Segmentation fault (thread 139907227260736, pid 16738)
Stack trace:
[baz()]
/proc/self/cwd/test/seg_fault.cc:33
 [__libc_start_main]
csu/libc-start.c:344
[_start]
seg_fault:0x40968a

[1]  + abort (core dumped) bazel run -c opt --copt=-g :segfault_binary

Other Options