Skip to content

Commit 1255dcd

Browse files
haoboxuAbhishek Kona
authored and
Abhishek Kona
committed
[RocksDB] Add stacktrace signal handler
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 facebook#1 0x40827d ./signal_test() [0x40827d] /home/haobo/rocksdb/util/signal_test.cc:24 facebook#2 0x7f8bb183172e /usr/local/fbcode/gcc-4.7.1-glibc-2.14.1/lib/libc.so.6(__libc_start_main+0x10e) [0x7f8bb183172e] ??:0 facebook#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
1 parent a29fc17 commit 1255dcd

File tree

5 files changed

+127
-11
lines changed

5 files changed

+127
-11
lines changed

Makefile

+5-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ TOOLS = \
6868
ldb \
6969
db_repl_stress
7070

71-
PROGRAMS = db_bench $(TESTS) $(TOOLS)
71+
PROGRAMS = db_bench signal_test $(TESTS) $(TOOLS)
7272
BENCHMARKS = db_bench_sqlite3 db_bench_tree_db
7373

7474
LIBRARY = librocksdb.a
@@ -155,6 +155,9 @@ db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL)
155155
db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL)
156156
$(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) -lkyotocabinet
157157

158+
signal_test: util/signal_test.o $(LIBOBJECTS)
159+
$(CXX) util/signal_test.o $(LIBOBJECTS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
160+
158161
arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
159162
$(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
160163

@@ -228,7 +231,7 @@ memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHAR
228231
rocksdb_shell: tools/shell/ShellContext.o tools/shell/ShellState.o tools/shell/LeveldbShell.o tools/shell/DBClientProxy.o tools/shell/ShellContext.h tools/shell/ShellState.h tools/shell/DBClientProxy.h $(LIBOBJECTS)
229232
$(CXX) tools/shell/ShellContext.o tools/shell/ShellState.o tools/shell/LeveldbShell.o tools/shell/DBClientProxy.o $(LIBOBJECTS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
230233

231-
DBClientProxy_test: tools/shell/test/DBClientProxyTest.o tools/shell/DBClientProxy.o $(LIBRARY)
234+
DBClientProxy_test: tools/shell/test/DBClientProxyTest.o tools/shell/DBClientProxy.o $(LIBRARY)
232235
$(CXX) tools/shell/test/DBClientProxyTest.o tools/shell/DBClientProxy.o $(LIBRARY) $(EXEC_LDFLAGS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
233236

234237
filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)

build_detect_platform

+12-9
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,17 @@ PLATFORM_SHARED_LDFLAGS="${EXEC_LDFLAGS_SHARED} -shared -Wl,-soname -Wl,"
6464
PLATFORM_SHARED_CFLAGS="-fPIC"
6565
PLATFORM_SHARED_VERSIONED=true
6666

67+
# generic port files (working on all platform by #ifdef) go directly in /port
68+
GENERIC_PORT_FILES=`find port -name '*.cc' | tr "\n" " "`
69+
6770
# On GCC, we pick libc's memcmp over GCC's memcmp via -fno-builtin-memcmp
6871
case "$TARGET_OS" in
6972
Darwin)
7073
PLATFORM=OS_MACOSX
7174
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -DOS_MACOSX"
7275
PLATFORM_SHARED_EXT=dylib
7376
PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name "
74-
PORT_FILE=port/port_posix.cc
77+
# PORT_FILES=port/darwin/darwin_specific.cc
7578
;;
7679
Linux)
7780
PLATFORM=OS_LINUX
@@ -80,43 +83,43 @@ case "$TARGET_OS" in
8083
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
8184
fi
8285
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
83-
PORT_FILE=port/port_posix.cc
86+
# PORT_FILES=port/linux/linux_specific.cc
8487
;;
8588
SunOS)
8689
PLATFORM=OS_SOLARIS
8790
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_SOLARIS"
8891
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
89-
PORT_FILE=port/port_posix.cc
92+
# PORT_FILES=port/sunos/sunos_specific.cc
9093
;;
9194
FreeBSD)
9295
PLATFORM=OS_FREEBSD
9396
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_FREEBSD"
9497
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
95-
PORT_FILE=port/port_posix.cc
98+
# PORT_FILES=port/freebsd/freebsd_specific.cc
9699
;;
97100
NetBSD)
98101
PLATFORM=OS_NETBSD
99102
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_NETBSD"
100103
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lgcc_s"
101-
PORT_FILE=port/port_posix.cc
104+
# PORT_FILES=port/netbsd/netbsd_specific.cc
102105
;;
103106
OpenBSD)
104107
PLATFORM=OS_OPENBSD
105108
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_OPENBSD"
106109
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -pthread"
107-
PORT_FILE=port/port_posix.cc
110+
# PORT_FILES=port/openbsd/openbsd_specific.cc
108111
;;
109112
DragonFly)
110113
PLATFORM=OS_DRAGONFLYBSD
111114
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_DRAGONFLYBSD"
112115
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
113-
PORT_FILE=port/port_posix.cc
116+
# PORT_FILES=port/dragonfly/dragonfly_specific.cc
114117
;;
115118
OS_ANDROID_CROSSCOMPILE)
116119
PLATFORM=OS_ANDROID
117120
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
118121
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS " # All pthread features are in the Android C library
119-
PORT_FILE=port/port_posix.cc
122+
# PORT_FILES=port/android/android.cc
120123
CROSS_COMPILE=true
121124
;;
122125
*)
@@ -149,7 +152,7 @@ set +f # re-enable globbing
149152

150153
# The sources consist of the portable files, plus the platform-specific port
151154
# file.
152-
echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT
155+
echo "SOURCES=$PORTABLE_FILES $GENERIC_PORT_FILES $PORT_FILES" >> $OUTPUT
153156
echo "SOURCESCPP=$PORTABLE_CPP" >> $OUTPUT
154157
echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT
155158

db/db_bench.cc

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "util/histogram.h"
2020
#include "util/mutexlock.h"
2121
#include "util/random.h"
22+
#include "util/stack_trace.h"
2223
#include "util/testutil.h"
2324
#include "hdfs/env_hdfs.h"
2425

@@ -1665,6 +1666,8 @@ unique_ptr<char []> GenerateKeyFromInt(int v)
16651666
} // namespace leveldb
16661667

16671668
int main(int argc, char** argv) {
1669+
leveldb::InstallStackTraceHandler();
1670+
16681671
FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
16691672
FLAGS_max_write_buffer_number = leveldb::Options().max_write_buffer_number;
16701673
FLAGS_open_files = leveldb::Options().max_open_files;

port/stack_trace.cc

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#include "util/stack_trace.h"
2+
3+
#ifdef OS_LINUX
4+
5+
#include <execinfo.h>
6+
#include <signal.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <string.h>
10+
#include <unistd.h>
11+
12+
namespace leveldb {
13+
14+
static const char* GetExecutableName()
15+
{
16+
static char name[1024];
17+
18+
char link[1024];
19+
snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
20+
auto read = readlink(link, name, sizeof(name));
21+
if (-1 == read) {
22+
return nullptr;
23+
} else {
24+
name[read] = 0;
25+
return name;
26+
}
27+
}
28+
29+
static void StackTraceHandler(int sig) {
30+
// reset to default handler
31+
signal(sig, SIG_DFL);
32+
33+
printf("Received signal %d (%s)\n", sig, strsignal(sig));
34+
35+
const int kMaxFrames = 100;
36+
void *frames[kMaxFrames];
37+
38+
auto num_frames = backtrace(frames, kMaxFrames);
39+
auto symbols = backtrace_symbols(frames, num_frames);
40+
41+
auto executable = GetExecutableName();
42+
43+
const int kSkip = 2; // skip the top two signal handler related frames
44+
45+
for (int i = kSkip; i < num_frames; ++i)
46+
{
47+
printf("#%-2d %p ", i - kSkip, frames[i]);
48+
if (symbols) {
49+
printf("%s ", symbols[i]);
50+
}
51+
if (executable) {
52+
// out source to addr2line, for the address translation
53+
const int kLineMax = 256;
54+
char cmd[kLineMax];
55+
sprintf(cmd,"addr2line %p -e %s 2>&1", frames[i] , executable);
56+
auto f = popen(cmd, "r");
57+
if (f) {
58+
char line[kLineMax];
59+
while (fgets(line, sizeof(line), f)) {
60+
printf("%s", line);
61+
}
62+
pclose(f);
63+
} else {
64+
printf("\n");
65+
}
66+
} else {
67+
printf("\n");
68+
}
69+
}
70+
71+
// re-signal to default handler (so we still get core dump if needed...)
72+
raise(sig);
73+
}
74+
75+
void InstallStackTraceHandler() {
76+
// just use the plain old signal as it's simple and sufficient
77+
// for this use case
78+
signal(SIGILL, StackTraceHandler);
79+
signal(SIGSEGV, StackTraceHandler);
80+
signal(SIGBUS, StackTraceHandler);
81+
signal(SIGABRT, StackTraceHandler);
82+
}
83+
84+
} // namespace leveldb
85+
86+
#else // no-op for non-linux system for now
87+
88+
namespace leveldb {
89+
90+
void InstallStackTraceHandler() {}
91+
92+
}
93+
94+
#endif // OS_LINUX

util/stack_trace.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef STACK_TRACE_H
2+
#define STACK_TRACE_H
3+
4+
namespace leveldb {
5+
6+
// Install a signal handler to print callstack on the following signals:
7+
// SIGILL SIGSEGV SIGBUS SIGABRT
8+
// Currently supports linux only. No-op otherwise.
9+
void InstallStackTraceHandler();
10+
11+
} // namespace leveldb
12+
13+
#endif

0 commit comments

Comments
 (0)