1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 #include "port/stack_trace.h"
7
8 #if defined(ROCKSDB_LITE) || \
9 !(defined(ROCKSDB_BACKTRACE) || defined(OS_MACOSX)) || defined(CYGWIN) || \
10 defined(OS_SOLARIS) || defined(OS_WIN)
11
12 // noop
13
14 namespace ROCKSDB_NAMESPACE {
15 namespace port {
InstallStackTraceHandler()16 void InstallStackTraceHandler() {}
PrintStack(int)17 void PrintStack(int /*first_frames_to_skip*/) {}
PrintAndFreeStack(void *,int)18 void PrintAndFreeStack(void* /*callstack*/, int /*num_frames*/) {}
SaveStack(int *,int)19 void* SaveStack(int* /*num_frames*/, int /*first_frames_to_skip*/) {
20 return nullptr;
21 }
22 } // namespace port
23 } // namespace ROCKSDB_NAMESPACE
24
25 #else
26
27 #include <execinfo.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <cxxabi.h>
34
35 #if defined(OS_FREEBSD)
36 #include <sys/sysctl.h>
37 #endif
38
39 #include "port/lang.h"
40
41 namespace ROCKSDB_NAMESPACE {
42 namespace port {
43
44 namespace {
45
46 #if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_GNU_KFREEBSD)
GetExecutableName()47 const char* GetExecutableName() {
48 static char name[1024];
49
50 #if !defined(OS_FREEBSD)
51 char link[1024];
52 snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
53 auto read = readlink(link, name, sizeof(name) - 1);
54 if (-1 == read) {
55 return nullptr;
56 } else {
57 name[read] = 0;
58 return name;
59 }
60 #else
61 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
62 size_t namesz = sizeof(name);
63
64 auto ret = sysctl(mib, 4, name, &namesz, nullptr, 0);
65 if (-1 == ret) {
66 return nullptr;
67 } else {
68 return name;
69 }
70 #endif
71 }
72
PrintStackTraceLine(const char * symbol,void * frame)73 void PrintStackTraceLine(const char* symbol, void* frame) {
74 static const char* executable = GetExecutableName();
75 if (symbol) {
76 fprintf(stderr, "%s ", symbol);
77 }
78 if (executable) {
79 // out source to addr2line, for the address translation
80 const int kLineMax = 256;
81 char cmd[kLineMax];
82 snprintf(cmd, kLineMax, "addr2line %p -e %s -f -C 2>&1", frame, executable);
83 auto f = popen(cmd, "r");
84 if (f) {
85 char line[kLineMax];
86 while (fgets(line, sizeof(line), f)) {
87 line[strlen(line) - 1] = 0; // remove newline
88 fprintf(stderr, "%s\t", line);
89 }
90 pclose(f);
91 }
92 } else {
93 fprintf(stderr, " %p", frame);
94 }
95
96 fprintf(stderr, "\n");
97 }
98 #elif defined(OS_MACOSX)
99
100 void PrintStackTraceLine(const char* symbol, void* frame) {
101 static int pid = getpid();
102 // out source to atos, for the address translation
103 const int kLineMax = 256;
104 char cmd[kLineMax];
105 snprintf(cmd, kLineMax, "xcrun atos %p -p %d 2>&1", frame, pid);
106 auto f = popen(cmd, "r");
107 if (f) {
108 char line[kLineMax];
109 while (fgets(line, sizeof(line), f)) {
110 line[strlen(line) - 1] = 0; // remove newline
111 fprintf(stderr, "%s\t", line);
112 }
113 pclose(f);
114 } else if (symbol) {
115 fprintf(stderr, "%s ", symbol);
116 }
117
118 fprintf(stderr, "\n");
119 }
120
121 #endif
122
123 } // namespace
124
PrintStack(void * frames[],int num_frames)125 void PrintStack(void* frames[], int num_frames) {
126 auto symbols = backtrace_symbols(frames, num_frames);
127
128 for (int i = 0; i < num_frames; ++i) {
129 fprintf(stderr, "#%-2d ", i);
130 PrintStackTraceLine((symbols != nullptr) ? symbols[i] : nullptr, frames[i]);
131 }
132 free(symbols);
133 }
134
PrintStack(int first_frames_to_skip)135 void PrintStack(int first_frames_to_skip) {
136 const int kMaxFrames = 100;
137 void* frames[kMaxFrames];
138
139 auto num_frames = backtrace(frames, kMaxFrames);
140 PrintStack(&frames[first_frames_to_skip], (int) (num_frames - first_frames_to_skip));
141 }
142
PrintAndFreeStack(void * callstack,int num_frames)143 void PrintAndFreeStack(void* callstack, int num_frames) {
144 PrintStack(static_cast<void**>(callstack), num_frames);
145 free(callstack);
146 }
147
SaveStack(int * num_frames,int first_frames_to_skip)148 void* SaveStack(int* num_frames, int first_frames_to_skip) {
149 const int kMaxFrames = 100;
150 void* frames[kMaxFrames];
151
152 auto count = backtrace(frames, kMaxFrames);
153 *num_frames = (int) (count - first_frames_to_skip);
154 void* callstack = malloc(sizeof(void*) * *num_frames);
155 memcpy(callstack, &frames[first_frames_to_skip], sizeof(void*) * *num_frames);
156 return callstack;
157 }
158
StackTraceHandler(int sig)159 static void StackTraceHandler(int sig) {
160 // reset to default handler
161 signal(sig, SIG_DFL);
162 fprintf(stderr, "Received signal %d (%s)\n", sig, strsignal(sig));
163 // skip the top three signal handler related frames
164 PrintStack(3);
165
166 // Efforts to fix or suppress TSAN warnings "signal-unsafe call inside of
167 // a signal" have failed, so just warn the user about them.
168 #ifdef __SANITIZE_THREAD__
169 fprintf(stderr,
170 "==> NOTE: any above warnings about \"signal-unsafe call\" are\n"
171 "==> ignorable, as they are expected when generating a stack\n"
172 "==> trace because of a signal under TSAN. Consider why the\n"
173 "==> signal was generated to begin with, and the stack trace\n"
174 "==> in the TSAN warning can be useful for that. (The stack\n"
175 "==> trace printed by the signal handler is likely obscured\n"
176 "==> by TSAN output.)\n");
177 #endif
178
179 // re-signal to default handler (so we still get core dump if needed...)
180 raise(sig);
181 }
182
InstallStackTraceHandler()183 void InstallStackTraceHandler() {
184 // just use the plain old signal as it's simple and sufficient
185 // for this use case
186 signal(SIGILL, StackTraceHandler);
187 signal(SIGSEGV, StackTraceHandler);
188 signal(SIGBUS, StackTraceHandler);
189 signal(SIGABRT, StackTraceHandler);
190 }
191
192 } // namespace port
193 } // namespace ROCKSDB_NAMESPACE
194
195 #endif
196