Commit ff77522a authored by Martin Martin's avatar Martin Martin Committed by Facebook Github Bot

Expose folly::symbolizer::dumpStackTrace().

Summary:
Expose folly::symbolizer::dumpStackTrace() for use with
custom signal handlers.

Reviewed By: luciang

Differential Revision: D4174004

fbshipit-source-id: 510b77edef652f3e9d10f0acfb4998b64a15fad5
parent ae7e1fd0
......@@ -30,7 +30,7 @@ namespace folly { namespace symbolizer {
size_t countLoadedElfFiles() {
// _r_debug synchronization is... lacking to say the least. It's
// meant as an aid for debuggers and synchrnization is done by
// meant as an aid for debuggers and synchronization is done by
// calling dl_debug_state() which debuggers are supposed to
// intercept by setting a breakpoint on.
......
......@@ -131,28 +131,12 @@ void callPreviousSignalHandler(int signum) {
// in our signal handler at a time.
//
// Leak it so we don't have to worry about destruction order
constexpr size_t kMinSignalSafeElfCacheSize = 500;
auto gSignalSafeElfCache = new SignalSafeElfCache(
std::max(countLoadedElfFiles(), kMinSignalSafeElfCacheSize));
// Buffered writer (using a fixed-size buffer). We try to write only once
// to prevent interleaving with messages written from other threads.
//
// Leak it so we don't have to worry about destruction order.
auto gPrinter = new FDSymbolizePrinter(STDERR_FILENO,
SymbolizePrinter::COLOR_IF_TTY,
size_t(64) << 10); // 64KiB
// Flush gPrinter, also fsync, in case we're about to crash again...
void flush() {
gPrinter->flush();
fsyncNoInt(STDERR_FILENO);
}
StackTracePrinter* gStackTracePrinter = new StackTracePrinter();
void printDec(uint64_t val) {
char buf[20];
uint32_t n = uint64ToBufferUnsafe(val, buf);
gPrinter->print(StringPiece(buf, n));
gStackTracePrinter->print(StringPiece(buf, n));
}
const char kHexChars[] = "0123456789abcdef";
......@@ -169,11 +153,15 @@ void printHex(uint64_t val) {
*--p = 'x';
*--p = '0';
gPrinter->print(StringPiece(p, end));
gStackTracePrinter->print(StringPiece(p, end));
}
void print(StringPiece sp) {
gPrinter->print(sp);
gStackTracePrinter->print(sp);
}
void flush() {
gStackTracePrinter->flush();
}
void dumpTimeInfo() {
......@@ -384,39 +372,6 @@ void dumpSignalInfo(int signum, siginfo_t* siginfo) {
print("), stack trace: ***\n");
}
FOLLY_NOINLINE void dumpStackTrace(bool symbolize);
void dumpStackTrace(bool symbolize) {
SCOPE_EXIT { flush(); };
// Get and symbolize stack trace
constexpr size_t kMaxStackTraceDepth = 100;
FrameArray<kMaxStackTraceDepth> addresses;
// Skip the getStackTrace frame
if (!getStackTraceSafe(addresses)) {
print("(error retrieving stack trace)\n");
} else if (symbolize) {
// Do our best to populate location info, process is going to terminate,
// so performance isn't critical.
Symbolizer symbolizer(gSignalSafeElfCache, Dwarf::LocationInfoMode::FULL);
symbolizer.symbolize(addresses);
// Skip the top 2 frames:
// getStackTraceSafe
// dumpStackTrace (here)
//
// Leaving signalHandler on the stack for clarity, I think.
gPrinter->println(addresses, 2);
} else {
print("(safe mode, symbolizer not available)\n");
AddressFormatter formatter;
for (size_t i = 0; i < addresses.frameCount; ++i) {
print(formatter.format(addresses.addresses[i]));
print("\n");
}
}
}
// On Linux, pthread_t is a pointer, so 0 is an invalid value, which we
// take to indicate "no thread in the signal handler".
//
......@@ -439,7 +394,7 @@ void innerSignalHandler(int signum, siginfo_t* info, void* /* uctx */) {
// next time around.
if (!gInRecursiveSignalHandler.exchange(true)) {
print("Entered fatal signal handler recursively. We're in trouble.\n");
dumpStackTrace(false); // no symbolization
gStackTracePrinter->printStackTrace(false); // no symbolization
}
return;
}
......@@ -455,7 +410,7 @@ void innerSignalHandler(int signum, siginfo_t* info, void* /* uctx */) {
dumpTimeInfo();
dumpSignalInfo(signum, info);
dumpStackTrace(true); // with symbolization
gStackTracePrinter->printStackTrace(true); // with symbolization
// Run user callbacks
gFatalSignalCallbackRegistry->run();
......
......@@ -48,5 +48,4 @@ void addFatalSignalCallback(SignalCallback callback);
*/
void installFatalSignalCallbacks();
}} // namespaces
......@@ -377,5 +377,51 @@ void StringSymbolizePrinter::doPrint(StringPiece sp) {
buf_.append(sp.data(), sp.size());
}
} // namespace symbolizer
StackTracePrinter::StackTracePrinter(size_t minSignalSafeElfCacheSize, int fd)
: fd_(fd),
elfCache_(std::max(countLoadedElfFiles(), minSignalSafeElfCacheSize)),
printer_(
fd,
SymbolizePrinter::COLOR_IF_TTY,
size_t(64) << 10) // 64KiB
{}
void StackTracePrinter::flush() {
printer_.flush();
fsyncNoInt(fd_);
}
void StackTracePrinter::printStackTrace(bool symbolize) {
SCOPE_EXIT {
flush();
};
// Get and symbolize stack trace
constexpr size_t kMaxStackTraceDepth = 100;
FrameArray<kMaxStackTraceDepth> addresses;
// Skip the getStackTrace frame
if (!getStackTraceSafe(addresses)) {
print("(error retrieving stack trace)\n");
} else if (symbolize) {
// Do our best to populate location info, process is going to terminate,
// so performance isn't critical.
Symbolizer symbolizer(&elfCache_, Dwarf::LocationInfoMode::FULL);
symbolizer.symbolize(addresses);
// Skip the top 2 frames:
// getStackTraceSafe
// StackTracePrinter::printStackTrace (here)
//
// Leaving signalHandler on the stack for clarity, I think.
printer_.println(addresses, 2);
} else {
print("(safe mode, symbolizer not available)\n");
AddressFormatter formatter;
for (size_t i = 0; i < addresses.frameCount; ++i) {
print(formatter.format(addresses.addresses[i]));
print("\n");
}
}
}
} // namespace symbolizer
} // namespace folly
......@@ -307,5 +307,46 @@ class StringSymbolizePrinter : public SymbolizePrinter {
fbstring buf_;
};
/**
* Use this class to print a stack trace from a signal handler, or other place
* where you shouldn't allocate memory on the heap, and fsync()ing your file
* descriptor is more important than performance.
*
* Make sure to create one of these on startup, not in the signal handler, as
* the constructo allocates on the heap, whereas the other methods don't. Best
* practice is to just leak this object, rather than worry about destruction
* order.
*
* These methods aren't thread safe, so if you could have signals on multiple
* threads at the same time, you need to do your own locking to ensure you don't
* call these methods from multiple threads. They are signal safe, however.
*/
class StackTracePrinter {
public:
static constexpr size_t kDefaultMinSignalSafeElfCacheSize = 500;
explicit StackTracePrinter(
size_t minSignalSafeElfCacheSize = kDefaultMinSignalSafeElfCacheSize,
int fd = STDERR_FILENO);
/**
* Only allocates on the stack and is signal-safe but not thread-safe. Don't
* call printStackTrace() on the same StackTracePrinter object from multiple
* threads at the same time.
*/
FOLLY_NOINLINE void printStackTrace(bool symbolize);
void print(StringPiece sp) {
printer_.print(sp);
}
// Flush printer_, also fsync, in case we're about to crash again...
void flush();
private:
int fd_;
SignalSafeElfCache elfCache_;
FDSymbolizePrinter printer_;
};
} // namespace symbolizer
} // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment