From: Martin Martin Date: Fri, 18 Nov 2016 17:59:37 +0000 (-0800) Subject: Expose folly::symbolizer::dumpStackTrace(). X-Git-Tag: v2016.11.21.00~7 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=ff77522af770ae066419df2aecefbc1338b8c7ab 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 --- diff --git a/folly/experimental/symbolizer/ElfCache.cpp b/folly/experimental/symbolizer/ElfCache.cpp index 4b7388f0..b5bf28d6 100644 --- a/folly/experimental/symbolizer/ElfCache.cpp +++ b/folly/experimental/symbolizer/ElfCache.cpp @@ -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. diff --git a/folly/experimental/symbolizer/SignalHandler.cpp b/folly/experimental/symbolizer/SignalHandler.cpp index 06173c08..16438f27 100644 --- a/folly/experimental/symbolizer/SignalHandler.cpp +++ b/folly/experimental/symbolizer/SignalHandler.cpp @@ -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 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(); diff --git a/folly/experimental/symbolizer/SignalHandler.h b/folly/experimental/symbolizer/SignalHandler.h index 726c8a81..8a8e1caf 100644 --- a/folly/experimental/symbolizer/SignalHandler.h +++ b/folly/experimental/symbolizer/SignalHandler.h @@ -48,5 +48,4 @@ void addFatalSignalCallback(SignalCallback callback); */ void installFatalSignalCallbacks(); - }} // namespaces diff --git a/folly/experimental/symbolizer/Symbolizer.cpp b/folly/experimental/symbolizer/Symbolizer.cpp index 13fe68fa..49451104 100644 --- a/folly/experimental/symbolizer/Symbolizer.cpp +++ b/folly/experimental/symbolizer/Symbolizer.cpp @@ -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 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 diff --git a/folly/experimental/symbolizer/Symbolizer.h b/folly/experimental/symbolizer/Symbolizer.h index bd93c121..9ce617b0 100644 --- a/folly/experimental/symbolizer/Symbolizer.h +++ b/folly/experimental/symbolizer/Symbolizer.h @@ -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