stack_trace.cc 5.4 KB
Newer Older
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
5
//
I
Igor Canadi 已提交
6
#include "port/stack_trace.h"
7

8 9
#if defined(ROCKSDB_LITE) ||                                                  \
    !(defined(ROCKSDB_BACKTRACE) || defined(OS_MACOSX)) || defined(CYGWIN) || \
10
    defined(OS_SOLARIS) || defined(OS_WIN)
I
Igor Canadi 已提交
11 12 13

// noop

14
namespace ROCKSDB_NAMESPACE {
I
Igor Canadi 已提交
15
namespace port {
I
Igor Canadi 已提交
16
void InstallStackTraceHandler() {}
A
Andrew Kryczka 已提交
17
void PrintStack(int /*first_frames_to_skip*/) {}
A
anand76 已提交
18 19 20 21
void PrintAndFreeStack(void* /*callstack*/, int /*num_frames*/) {}
void* SaveStack(int* /*num_frames*/, int /*first_frames_to_skip*/) {
  return nullptr;
}
I
Igor Canadi 已提交
22
}  // namespace port
23
}  // namespace ROCKSDB_NAMESPACE
I
Igor Canadi 已提交
24 25

#else
26 27 28 29 30 31 32

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
33
#include <cxxabi.h>
34

35 36 37 38
#if defined(OS_FREEBSD)
#include <sys/sysctl.h>
#endif

39
namespace ROCKSDB_NAMESPACE {
I
Igor Canadi 已提交
40 41
namespace port {

I
Igor Canadi 已提交
42
namespace {
43

44
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_GNU_KFREEBSD)
I
Igor Canadi 已提交
45
const char* GetExecutableName() {
46 47
  static char name[1024];

48
#if !defined(OS_FREEBSD)
49 50
  char link[1024];
  snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
51
  auto read = readlink(link, name, sizeof(name) - 1);
52 53 54 55 56 57
  if (-1 == read) {
    return nullptr;
  } else {
    name[read] = 0;
    return name;
  }
58 59 60 61 62 63 64 65 66 67 68
#else
  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
  size_t namesz = sizeof(name);

  auto ret = sysctl(mib, 4, name, &namesz, nullptr, 0);
  if (-1 == ret) {
    return nullptr;
  } else {
    return name;
  }
#endif
69 70
}

I
Igor Canadi 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
void PrintStackTraceLine(const char* symbol, void* frame) {
  static const char* executable = GetExecutableName();
  if (symbol) {
    fprintf(stderr, "%s ", symbol);
  }
  if (executable) {
    // out source to addr2line, for the address translation
    const int kLineMax = 256;
    char cmd[kLineMax];
    snprintf(cmd, kLineMax, "addr2line %p -e %s -f -C 2>&1", frame, executable);
    auto f = popen(cmd, "r");
    if (f) {
      char line[kLineMax];
      while (fgets(line, sizeof(line), f)) {
        line[strlen(line) - 1] = 0;  // remove newline
        fprintf(stderr, "%s\t", line);
      }
      pclose(f);
    }
  } else {
    fprintf(stderr, " %p", frame);
  }

  fprintf(stderr, "\n");
}
96
#elif defined(OS_MACOSX)
I
Igor Canadi 已提交
97 98

void PrintStackTraceLine(const char* symbol, void* frame) {
I
Igor Canadi 已提交
99 100 101 102
  static int pid = getpid();
  // out source to atos, for the address translation
  const int kLineMax = 256;
  char cmd[kLineMax];
I
Igor Canadi 已提交
103
  snprintf(cmd, kLineMax, "xcrun atos %p -p %d  2>&1", frame, pid);
I
Igor Canadi 已提交
104 105 106 107 108 109
  auto f = popen(cmd, "r");
  if (f) {
    char line[kLineMax];
    while (fgets(line, sizeof(line), f)) {
      line[strlen(line) - 1] = 0;  // remove newline
      fprintf(stderr, "%s\t", line);
110
    }
I
Igor Canadi 已提交
111 112 113
    pclose(f);
  } else if (symbol) {
    fprintf(stderr, "%s ", symbol);
I
Igor Canadi 已提交
114
  }
I
Igor Canadi 已提交
115

I
Igor Canadi 已提交
116 117 118 119 120 121 122
  fprintf(stderr, "\n");
}

#endif

}  // namespace

A
anand76 已提交
123 124 125 126 127 128 129 130 131 132
void PrintStack(void* frames[], int num_frames) {
  auto symbols = backtrace_symbols(frames, num_frames);

  for (int i = 0; i < num_frames; ++i) {
    fprintf(stderr, "#%-2d  ", i);
    PrintStackTraceLine((symbols != nullptr) ? symbols[i] : nullptr, frames[i]);
  }
  free(symbols);
}

133
void PrintStack(int first_frames_to_skip) {
134
  const int kMaxFrames = 100;
I
Igor Canadi 已提交
135
  void* frames[kMaxFrames];
136 137

  auto num_frames = backtrace(frames, kMaxFrames);
A
anand76 已提交
138 139
  PrintStack(&frames[first_frames_to_skip], num_frames - first_frames_to_skip);
}
140

A
anand76 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154
void PrintAndFreeStack(void* callstack, int num_frames) {
  PrintStack(static_cast<void**>(callstack), num_frames);
  free(callstack);
}

void* SaveStack(int* num_frames, int first_frames_to_skip) {
  const int kMaxFrames = 100;
  void* frames[kMaxFrames];

  auto count = backtrace(frames, kMaxFrames);
  *num_frames = count - first_frames_to_skip;
  void* callstack = malloc(sizeof(void*) * *num_frames);
  memcpy(callstack, &frames[first_frames_to_skip], sizeof(void*) * *num_frames);
  return callstack;
155
}
156

157 158 159 160 161 162
static void StackTraceHandler(int sig) {
  // reset to default handler
  signal(sig, SIG_DFL);
  fprintf(stderr, "Received signal %d (%s)\n", sig, strsignal(sig));
  // skip the top three signal handler related frames
  PrintStack(3);
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

  // Efforts to fix or suppress TSAN warnings "signal-unsafe call inside of
  // a signal" have failed, so just warn the user about them.
#if defined(__clang__) && defined(__has_feature)
#if __has_feature(thread_sanitizer)
  fprintf(stderr,
          "==> NOTE: any above warnings about \"signal-unsafe call\" are\n"
          "==> ignorable, as they are expected when generating a stack\n"
          "==> trace because of a signal under TSAN. Consider why the\n"
          "==> signal was generated to begin with, and the stack trace\n"
          "==> in the TSAN warning can be useful for that. (The stack\n"
          "==> trace printed by the signal handler is likely obscured\n"
          "==> by TSAN output.)\n");
#endif
#endif

179 180 181 182 183 184 185 186 187 188 189 190 191
  // 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);
}

I
Igor Canadi 已提交
192
}  // namespace port
193
}  // namespace ROCKSDB_NAMESPACE
I
Igor Canadi 已提交
194 195

#endif