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) || !(defined(ROCKSDB_BACKTRACE) || defined(OS_MACOSX)) || \
9     defined(CYGWIN) || defined(OS_FREEBSD) || defined(OS_SOLARIS)
10 
11 // noop
12 
13 namespace rocksdb {
14 namespace port {
InstallStackTraceHandler()15 void InstallStackTraceHandler() {}
PrintStack(int)16 void PrintStack(int /*first_frames_to_skip*/) {}
17 }  // namespace port
18 }  // namespace rocksdb
19 
20 #else
21 
22 #include <execinfo.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <cxxabi.h>
29 
30 namespace rocksdb {
31 namespace port {
32 
33 namespace {
34 
35 #if defined(OS_LINUX) || defined(OS_FREEBSD)
GetExecutableName()36 const char* GetExecutableName() {
37   static char name[1024];
38 
39   char link[1024];
40   snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
41   auto read = readlink(link, name, sizeof(name) - 1);
42   if (-1 == read) {
43     return nullptr;
44   } else {
45     name[read] = 0;
46     return name;
47   }
48 }
49 
PrintStackTraceLine(const char * symbol,void * frame)50 void PrintStackTraceLine(const char* symbol, void* frame) {
51   static const char* executable = GetExecutableName();
52   if (symbol) {
53     fprintf(stderr, "%s ", symbol);
54   }
55   if (executable) {
56     // out source to addr2line, for the address translation
57     const int kLineMax = 256;
58     char cmd[kLineMax];
59     snprintf(cmd, kLineMax, "addr2line %p -e %s -f -C 2>&1", frame, executable);
60     auto f = popen(cmd, "r");
61     if (f) {
62       char line[kLineMax];
63       while (fgets(line, sizeof(line), f)) {
64         line[strlen(line) - 1] = 0;  // remove newline
65         fprintf(stderr, "%s\t", line);
66       }
67       pclose(f);
68     }
69   } else {
70     fprintf(stderr, " %p", frame);
71   }
72 
73   fprintf(stderr, "\n");
74 }
75 #elif defined(OS_MACOSX)
76 
77 void PrintStackTraceLine(const char* symbol, void* frame) {
78   static int pid = getpid();
79   // out source to atos, for the address translation
80   const int kLineMax = 256;
81   char cmd[kLineMax];
82   snprintf(cmd, kLineMax, "xcrun atos %p -p %d  2>&1", frame, pid);
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   } else if (symbol) {
92     fprintf(stderr, "%s ", symbol);
93   }
94 
95   fprintf(stderr, "\n");
96 }
97 
98 #endif
99 
100 }  // namespace
101 
PrintStack(int first_frames_to_skip)102 void PrintStack(int first_frames_to_skip) {
103   const int kMaxFrames = 100;
104   void* frames[kMaxFrames];
105 
106   auto num_frames = backtrace(frames, kMaxFrames);
107   auto symbols = backtrace_symbols(frames, num_frames);
108 
109   for (int i = first_frames_to_skip; i < num_frames; ++i) {
110     fprintf(stderr, "#%-2d  ", i - first_frames_to_skip);
111     PrintStackTraceLine((symbols != nullptr) ? symbols[i] : nullptr, frames[i]);
112   }
113   free(symbols);
114 }
115 
StackTraceHandler(int sig)116 static void StackTraceHandler(int sig) {
117   // reset to default handler
118   signal(sig, SIG_DFL);
119   fprintf(stderr, "Received signal %d (%s)\n", sig, strsignal(sig));
120   // skip the top three signal handler related frames
121   PrintStack(3);
122   // re-signal to default handler (so we still get core dump if needed...)
123   raise(sig);
124 }
125 
InstallStackTraceHandler()126 void InstallStackTraceHandler() {
127   // just use the plain old signal as it's simple and sufficient
128   // for this use case
129   signal(SIGILL, StackTraceHandler);
130   signal(SIGSEGV, StackTraceHandler);
131   signal(SIGBUS, StackTraceHandler);
132   signal(SIGABRT, StackTraceHandler);
133 }
134 
135 }  // namespace port
136 }  // namespace rocksdb
137 
138 #endif
139