mirror of
https://github.com/facebook/rocksdb.git
synced 2024-12-02 10:15:54 +00:00
0b06af9146
Summary: TSAN reports that our stack trace handler makes unsafe calls during a signal handler. I just tried fixing some of them and I don't think it's fixable unless we can get away from using FILE stdio. Even if we can use lower level functions only, I'm not sure it's fixed. I also tried suppressing the reports with function and file level TSAN suppression, but that doesn't seem to work, perhaps because the violation is reported on the callee, not the caller. So I added a warning to be printed whenever these violations would be reported that they are practically ignorable. Internal ref: T77844138 Pull Request resolved: https://github.com/facebook/rocksdb/pull/7723 Test Plan: run external_sst_file_test with seeded abort(), with TSAN (TSAN warnings + new warning) and without TSAN (no warning, just stack trace). Reviewed By: akankshamahajan15 Differential Revision: D25228011 Pulled By: pdillinger fbshipit-source-id: 3eda1d6e7ca3cdc64076cf99ae954168837d2818
180 lines
5.1 KiB
C++
180 lines
5.1 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// 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).
|
|
//
|
|
#include "port/stack_trace.h"
|
|
|
|
#if defined(ROCKSDB_LITE) || \
|
|
!(defined(ROCKSDB_BACKTRACE) || defined(OS_MACOSX)) || defined(CYGWIN) || \
|
|
defined(OS_FREEBSD) || defined(OS_SOLARIS) || defined(OS_WIN)
|
|
|
|
// noop
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
namespace port {
|
|
void InstallStackTraceHandler() {}
|
|
void PrintStack(int /*first_frames_to_skip*/) {}
|
|
void PrintAndFreeStack(void* /*callstack*/, int /*num_frames*/) {}
|
|
void* SaveStack(int* /*num_frames*/, int /*first_frames_to_skip*/) {
|
|
return nullptr;
|
|
}
|
|
} // namespace port
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
#else
|
|
|
|
#include <execinfo.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <cxxabi.h>
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
namespace port {
|
|
|
|
namespace {
|
|
|
|
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_GNU_KFREEBSD)
|
|
const char* GetExecutableName() {
|
|
static char name[1024];
|
|
|
|
char link[1024];
|
|
snprintf(link, sizeof(link), "/proc/%d/exe", getpid());
|
|
auto read = readlink(link, name, sizeof(name) - 1);
|
|
if (-1 == read) {
|
|
return nullptr;
|
|
} else {
|
|
name[read] = 0;
|
|
return name;
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
#elif defined(OS_MACOSX)
|
|
|
|
void PrintStackTraceLine(const char* symbol, void* frame) {
|
|
static int pid = getpid();
|
|
// out source to atos, for the address translation
|
|
const int kLineMax = 256;
|
|
char cmd[kLineMax];
|
|
snprintf(cmd, kLineMax, "xcrun atos %p -p %d 2>&1", frame, pid);
|
|
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 if (symbol) {
|
|
fprintf(stderr, "%s ", symbol);
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace
|
|
|
|
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);
|
|
}
|
|
|
|
void PrintStack(int first_frames_to_skip) {
|
|
const int kMaxFrames = 100;
|
|
void* frames[kMaxFrames];
|
|
|
|
auto num_frames = backtrace(frames, kMaxFrames);
|
|
PrintStack(&frames[first_frames_to_skip], num_frames - first_frames_to_skip);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
// 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
|
|
|
|
// 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);
|
|
}
|
|
|
|
} // namespace port
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
#endif
|