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
- libbacktrace need to try out. Uses libunwind.
- libunwind
- Google Breakpad.
- Abseil - Wish it resolved line numbers and worked with exceptions.