1255dcd446
Summary: This diff provides the ability to print out a stacktrace when the process receives certain signals. Currently, we enable this for the following signals (program error related): SIGILL SIGSEGV SIGBUS SIGABRT Application simply #include "util/stack_trace.h" and call leveldb::InstallStackTraceHandler() during initialization, if signal handler is needed. It's not done automatically when openning db, because it's the application(process)'s responsibility to install signal handler and some applications might already have their own (like fbcode). Sample output: Received signal 11 (Segmentation fault) #0 0x408ff0 ./signal_test() [0x408ff0] /home/haobo/rocksdb/util/signal_test.cc:4 #1 0x40827d ./signal_test() [0x40827d] /home/haobo/rocksdb/util/signal_test.cc:24 #2 0x7f8bb183172e /usr/local/fbcode/gcc-4.7.1-glibc-2.14.1/lib/libc.so.6(__libc_start_main+0x10e) [0x7f8bb183172e] ??:0 #3 0x408ebc ./signal_test() [0x408ebc] /home/engshare/third-party/src/glibc/glibc-2.14.1/glibc-2.14.1/csu/../sysdeps/x86_64/elf/start.S:113 Segmentation fault (core dumped) For each frame, we print the raw pointer, the symbol provided by backtrace_symbols (still not good enough), and the source file/line. Note that address translation is done by directly shell out to addr2line. ??:0 means addr2line fails to do the translation. Hacky, but I think it's good for now. Test Plan: signal_test.cc Reviewers: dhruba, MarkCallaghan Reviewed By: dhruba CC: leveldb Differential Revision: https://reviews.facebook.net/D10173
95 lines
2.0 KiB
C++
95 lines
2.0 KiB
C++
#include "util/stack_trace.h"
|
|
|
|
#ifdef OS_LINUX
|
|
|
|
#include <execinfo.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
namespace leveldb {
|
|
|
|
static const char* GetExecutableName()
|
|
{
|
|
static char name[1024];
|
|
|
|
char link[1024];
|
|
snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
|
|
auto read = readlink(link, name, sizeof(name));
|
|
if (-1 == read) {
|
|
return nullptr;
|
|
} else {
|
|
name[read] = 0;
|
|
return name;
|
|
}
|
|
}
|
|
|
|
static void StackTraceHandler(int sig) {
|
|
// reset to default handler
|
|
signal(sig, SIG_DFL);
|
|
|
|
printf("Received signal %d (%s)\n", sig, strsignal(sig));
|
|
|
|
const int kMaxFrames = 100;
|
|
void *frames[kMaxFrames];
|
|
|
|
auto num_frames = backtrace(frames, kMaxFrames);
|
|
auto symbols = backtrace_symbols(frames, num_frames);
|
|
|
|
auto executable = GetExecutableName();
|
|
|
|
const int kSkip = 2; // skip the top two signal handler related frames
|
|
|
|
for (int i = kSkip; i < num_frames; ++i)
|
|
{
|
|
printf("#%-2d %p ", i - kSkip, frames[i]);
|
|
if (symbols) {
|
|
printf("%s ", symbols[i]);
|
|
}
|
|
if (executable) {
|
|
// out source to addr2line, for the address translation
|
|
const int kLineMax = 256;
|
|
char cmd[kLineMax];
|
|
sprintf(cmd,"addr2line %p -e %s 2>&1", frames[i] , executable);
|
|
auto f = popen(cmd, "r");
|
|
if (f) {
|
|
char line[kLineMax];
|
|
while (fgets(line, sizeof(line), f)) {
|
|
printf("%s", line);
|
|
}
|
|
pclose(f);
|
|
} else {
|
|
printf("\n");
|
|
}
|
|
} else {
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
// re-signal to default handler (so we still get core dump if needed...)
|
|
raise(sig);
|
|
}
|
|
|
|
void InstallStackTraceHandler() {
|
|
// just use the plain old signal as it's simple and sufficient
|
|
// for this use case
|
|
signal(SIGILL, StackTraceHandler);
|
|
signal(SIGSEGV, StackTraceHandler);
|
|
signal(SIGBUS, StackTraceHandler);
|
|
signal(SIGABRT, StackTraceHandler);
|
|
}
|
|
|
|
} // namespace leveldb
|
|
|
|
#else // no-op for non-linux system for now
|
|
|
|
namespace leveldb {
|
|
|
|
void InstallStackTraceHandler() {}
|
|
|
|
}
|
|
|
|
#endif // OS_LINUX
|