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