Expose folly::symbolizer::dumpStackTrace().
authorMartin Martin <mcm@fb.com>
Fri, 18 Nov 2016 17:59:37 +0000 (09:59 -0800)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Fri, 18 Nov 2016 18:08:29 +0000 (10:08 -0800)
Summary:
Expose folly::symbolizer::dumpStackTrace() for use with
custom signal handlers.

Reviewed By: luciang

Differential Revision: D4174004

fbshipit-source-id: 510b77edef652f3e9d10f0acfb4998b64a15fad5

folly/experimental/symbolizer/ElfCache.cpp
folly/experimental/symbolizer/SignalHandler.cpp
folly/experimental/symbolizer/SignalHandler.h
folly/experimental/symbolizer/Symbolizer.cpp
folly/experimental/symbolizer/Symbolizer.h

index 4b7388f0a60c27191dc5dcac7ac3f621496e90dc..b5bf28d6cf77ca779f56209efd9f9e7baf1d85ec 100644 (file)
@@ -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.
 
index 06173c08f2be89d997f0e22fcc40fafe349158e6..16438f27ac94640ace11465a2389ee822fb12f91 100644 (file)
@@ -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();
index 726c8a81e707bf10c1dc156485e5252ccad501cf..8a8e1caff8cdc338cd559f19e0fa397bfd4afa5c 100644 (file)
@@ -48,5 +48,4 @@ void addFatalSignalCallback(SignalCallback callback);
  */
 void installFatalSignalCallbacks();
 
-
 }}  // namespaces
index 13fe68fa38d4ab054f7b039ccbd301a1353d0941..49451104a08bc1bd89f77ad698f5f427a301301a 100644 (file)
@@ -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
index bd93c121f42195fe62aa2057991cd951797a3659..9ce617b071a6d0ae3783813ba3a0321a99f810f1 100644 (file)
@@ -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