stack_trace.cc 5.3 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 40
#include "port/lang.h"

41
namespace ROCKSDB_NAMESPACE {
I
Igor Canadi 已提交
42 43
namespace port {

I
Igor Canadi 已提交
44
namespace {
45

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

50
#if !defined(OS_FREEBSD)
51 52
  char link[1024];
  snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
53
  auto read = readlink(link, name, sizeof(name) - 1);
54 55 56 57 58 59
  if (-1 == read) {
    return nullptr;
  } else {
    name[read] = 0;
    return name;
  }
60 61 62 63 64 65 66 67 68 69 70
#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
71 72
}

I
Igor Canadi 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
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");
}
98
#elif defined(OS_MACOSX)
I
Igor Canadi 已提交
99 100

void PrintStackTraceLine(const char* symbol, void* frame) {
I
Igor Canadi 已提交
101 102 103 104
  static int pid = getpid();
  // out source to atos, for the address translation
  const int kLineMax = 256;
  char cmd[kLineMax];
I
Igor Canadi 已提交
105
  snprintf(cmd, kLineMax, "xcrun atos %p -p %d  2>&1", frame, pid);
I
Igor Canadi 已提交
106 107 108 109 110 111
  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);
112
    }
I
Igor Canadi 已提交
113 114 115
    pclose(f);
  } else if (symbol) {
    fprintf(stderr, "%s ", symbol);
I
Igor Canadi 已提交
116
  }
I
Igor Canadi 已提交
117

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

#endif

}  // namespace

A
anand76 已提交
125 126 127 128 129 130 131 132 133 134
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);
}

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

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

A
anand76 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156
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;
157
}
158

159 160 161 162 163 164
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);
165 166 167

  // Efforts to fix or suppress TSAN warnings "signal-unsafe call inside of
  // a signal" have failed, so just warn the user about them.
168
#ifdef __SANITIZE_THREAD__
169 170 171 172 173 174 175 176 177 178
  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

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