Print (2 more) if stack trace truncated
authorTudor Bosman <tudorb@fb.com>
Tue, 3 Dec 2013 23:11:11 +0000 (15:11 -0800)
committerJordan DeLong <jdelong@fb.com>
Fri, 20 Dec 2013 21:04:42 +0000 (13:04 -0800)
Summary:
Also, C++ify the interface and switch to per-thread caching in libunwind as per
D1081259

Test Plan: folly/experimental/symbolizer/test

Reviewed By: lucian@fb.com

FB internal diff: D1081272

folly/experimental/exception_tracer/ExceptionTracer.cpp
folly/experimental/symbolizer/SignalHandler.cpp
folly/experimental/symbolizer/Symbolizer.cpp
folly/experimental/symbolizer/Symbolizer.h
folly/experimental/symbolizer/test/SymbolizerTest.cpp

index 19b85ec2006ef1ddd8c63c94000d49439e68392a..2e84227a0b6bb3b471a0623d67be5f9125652326 100644 (file)
@@ -54,7 +54,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
       << (info.frames.size() == 1 ? " frame" : " frames")
       << ")\n";
   try {
-    std::vector<AddressInfo> addresses;
+    std::vector<FrameInfo> addresses;
     addresses.reserve(info.frames.size());
     for (auto ip : info.frames) {
       // Symbolize the previous address because the IP might be in the
@@ -66,7 +66,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
     symbolizer.symbolize(addresses.data(), addresses.size());
 
     OStreamSymbolizePrinter osp(out);
-    osp.print(addresses.data(), addresses.size());
+    osp.print(addresses.data(), addresses.size(), addresses.size());
   } catch (const std::exception& e) {
     out << "\n !! caught " << folly::exceptionStr(e) << "\n";
   } catch (...) {
index f66d013373aa47cda6a9d42c442ff29c39609d73..58e8b18478d437d8927288c63cbb81abe416fcba 100644 (file)
@@ -193,18 +193,17 @@ void dumpStackTrace() {
   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
   // Get and symbolize stack trace
   constexpr size_t kMaxStackTraceDepth = 100;
-  AddressInfo addresses[kMaxStackTraceDepth];
+  FrameArray<kMaxStackTraceDepth> addresses;
 
   // Skip the getStackTrace frame
-  ssize_t stackTraceDepth = getStackTrace(addresses, kMaxStackTraceDepth, 1);
-  if (stackTraceDepth < 0) {
+  if (!getStackTrace(addresses)) {
     print("(error retrieving stack trace)\n");
   } else {
     Symbolizer symbolizer;
-    symbolizer.symbolize(addresses, stackTraceDepth);
+    symbolizer.symbolize(addresses);
 
     FDSymbolizePrinter printer(STDERR_FILENO);
-    printer.print(addresses, stackTraceDepth);
+    printer.print(addresses);
   }
 }
 
index 79345c8b6eee49670e697560f53f62d019f2943a..f7e0d59d232abe291385182c18735fd66659fd50 100644 (file)
@@ -148,7 +148,7 @@ bool parseProcMapsLine(StringPiece line,
 
 }  // namespace
 
-ssize_t getStackTrace(AddressInfo* addresses,
+ssize_t getStackTrace(FrameInfo* addresses,
                       size_t maxAddresses,
                       size_t skip) {
   unw_context_t uctx;
@@ -160,7 +160,7 @@ ssize_t getStackTrace(AddressInfo* addresses,
   unw_cursor_t cursor;
   size_t idx = 0;
   bool first = true;
-  while (idx < maxAddresses) {
+  for (;;) {
     if (first) {
       first = false;
       r = unw_init_local(&cursor, &uctx);
@@ -178,26 +178,25 @@ ssize_t getStackTrace(AddressInfo* addresses,
       --skip;
       continue;
     }
-    unw_word_t ip;
-    int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip);
-    if (rr < 0) {
-      return -1;
-    }
-
-    // If error, assume not a signal frame
-    rr = unw_is_signal_frame(&cursor);
 
-    addresses[idx++] = AddressInfo(ip, (rr > 0));
-  }
+    if (idx < maxAddresses) {
+      unw_word_t ip;
+      int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip);
+      if (rr < 0) {
+        return -1;
+      }
 
-  if (r < 0) {
-    return -1;
+      // If error, assume not a signal frame
+      rr = unw_is_signal_frame(&cursor);
+      addresses[idx] = FrameInfo(ip, (rr > 0));
+    }
+    ++idx;
   }
 
   return idx;
 }
 
-void Symbolizer::symbolize(AddressInfo* addresses, size_t addressCount) {
+void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) {
   size_t remaining = 0;
   for (size_t i = 0; i < addressCount; ++i) {
     auto& ainfo = addresses[i];
@@ -305,7 +304,7 @@ namespace {
 const char kHexChars[] = "0123456789abcdef";
 }  // namespace
 
-void SymbolizePrinter::print(const AddressInfo& ainfo) {
+void SymbolizePrinter::print(const FrameInfo& ainfo) {
   uintptr_t address = ainfo.address;
   // Can't use sprintf, not async-signal-safe
   static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
@@ -372,12 +371,22 @@ void SymbolizePrinter::print(const AddressInfo& ainfo) {
   }
 }
 
-void SymbolizePrinter::print(const AddressInfo* addresses,
-                             size_t addressCount) {
-  for (size_t i = 0; i < addressCount; ++i) {
+void SymbolizePrinter::print(const FrameInfo* addresses,
+                             size_t addressesSize,
+                             size_t frameCount) {
+  for (size_t i = 0; i < std::min(addressesSize, frameCount); ++i) {
     auto& ainfo = addresses[i];
     print(ainfo);
   }
+
+  // Indicate the number of frames that we couldn't log due to space
+  if (frameCount > addressesSize) {
+    char buf[22];
+    uint32_t n = uint64ToBufferUnsafe(frameCount - addressesSize, buf);
+    doPrint("    (");
+    doPrint(StringPiece(buf, n));
+    doPrint(" omitted, max buffer size reached)\n");
+  }
 }
 
 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
@@ -388,11 +397,29 @@ void FDSymbolizePrinter::doPrint(StringPiece sp) {
   writeFull(fd_, sp.data(), sp.size());
 }
 
-std::ostream& operator<<(std::ostream& out, const AddressInfo& ainfo) {
+std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo) {
   OStreamSymbolizePrinter osp(out);
   osp.print(ainfo);
   return out;
 }
 
+namespace {
+
+struct Init {
+  Init();
+};
+
+Init::Init() {
+  // Don't use global caching -- it's slow and leads to lock contention.  (And
+  // it's made signal-safe using sigprocmask to block all signals while the
+  // lock is being held, and sigprocmask contends on a lock inside the kernel,
+  // too, ugh.)
+  unw_set_caching_policy(unw_local_addr_space, UNW_CACHE_PER_THREAD);
+}
+
+Init initializer;
+
+}  // namespace
+
 }  // namespace symbolizer
 }  // namespace folly
index 307ca252dcd54fd6bf02bd7074a6ef99a5f61597..84913526917a36d32b2cf6f7c9711ae832848b72 100644 (file)
@@ -30,13 +30,13 @@ namespace folly {
 namespace symbolizer {
 
 /**
- * Address information: symbol name and location.
+ * Frame information: symbol name and location.
  *
  * Note that both name and location are references in the Symbolizer object,
- * which must outlive this AddressInfo object.
+ * which must outlive this FrameInfo object.
  */
-struct AddressInfo {
-  /* implicit */ AddressInfo(uintptr_t a=0, bool sf=false)
+struct FrameInfo {
+  /* implicit */ FrameInfo(uintptr_t a=0, bool sf=false)
     : address(a),
       isSignalFrame(sf),
       found(false) { }
@@ -47,15 +47,46 @@ struct AddressInfo {
   Dwarf::LocationInfo location;
 };
 
+template <size_t N>
+struct FrameArray {
+  FrameArray() : frameCount(0) { }
+
+  size_t frameCount;
+  FrameInfo frames[N];
+};
+
 /**
  * Get the current stack trace into addresses, which has room for at least
- * maxAddresses entries. Skip the first (topmost) skip entries.
- * Returns the number of entries in addresses on success, -1 on failure.
+ * maxAddresses frames. Skip the first (topmost) skip entries.
+ *
+ * Returns the number of frames in the stack trace. Just like snprintf,
+ * if the number of frames is greater than maxAddresses, it will return
+ * the actual number of frames, so the stack trace was truncated iff
+ * the return value > maxAddresses.
+ *
+ * Returns -1 on failure.
  */
-ssize_t getStackTrace(AddressInfo* addresses,
+ssize_t getStackTrace(FrameInfo* addresses,
                       size_t maxAddresses,
                       size_t skip=0);
 
+/**
+ * Get stack trace into a given FrameArray, return true on success (and
+ * set frameCount to the actual frame count, which may be > N) and false
+ * on failure.
+ */
+template <size_t N>
+bool getStackTrace(FrameArray<N>& fa, size_t skip=0) {
+  ssize_t n = getStackTrace(fa.frames, N, skip);
+  if (n != -1) {
+    fa.frameCount = n;
+    return true;
+  } else {
+    fa.frameCount = 0;
+    return false;
+  }
+}
+
 class Symbolizer {
  public:
   Symbolizer() : fileCount_(0) { }
@@ -63,12 +94,17 @@ class Symbolizer {
   /**
    * Symbolize given addresses.
    */
-  void symbolize(AddressInfo* addresses, size_t addressCount);
+  void symbolize(FrameInfo* addresses, size_t addressCount);
+
+  template <size_t N>
+  void symbolize(FrameArray<N>& fa) {
+    symbolize(fa.frames, std::min(fa.frameCount, N));
+  }
 
   /**
    * Shortcut to symbolize one address.
    */
-  bool symbolize(AddressInfo& address) {
+  bool symbolize(FrameInfo& address) {
     symbolize(&address, 1);
     return address.found;
   }
@@ -86,8 +122,15 @@ class Symbolizer {
  */
 class SymbolizePrinter {
  public:
-  void print(const AddressInfo& ainfo);
-  void print(const AddressInfo* addresses, size_t addressCount);
+  void print(const FrameInfo& ainfo);
+  void print(const FrameInfo* addresses,
+             size_t addressesSize,
+             size_t frameCount);
+
+  template <size_t N>
+  void print(const FrameArray<N>& fa) {
+    print(fa.frames, N, fa.frameCount);
+  }
 
   virtual ~SymbolizePrinter() { }
  private:
@@ -119,12 +162,12 @@ class FDSymbolizePrinter : public SymbolizePrinter {
 };
 
 /**
- * Print an AddressInfo to a stream. Note that the Symbolizer that
- * symbolized the address must outlive the AddressInfo. Just like
+ * Print an FrameInfo to a stream. Note that the Symbolizer that
+ * symbolized the address must outlive the FrameInfo. Just like
  * OStreamSymbolizePrinter (which it uses internally), this is not
  * reentrant; do not use from signal handling code.
  */
-std::ostream& operator<<(std::ostream& out, const AddressInfo& ainfo);
+std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo);
 
 }  // namespace symbolizer
 }  // namespace folly
index 0ff9cfde20d65683841df4787c738ffd2e0f8423..e9a0967482ef4ebf1740d5b7f1ab3248a1205f67 100644 (file)
@@ -27,7 +27,7 @@ void foo() {
 }
 
 TEST(Symbolizer, Single) {
-  AddressInfo a(reinterpret_cast<uintptr_t>(foo));
+  FrameInfo a(reinterpret_cast<uintptr_t>(foo));
   Symbolizer symbolizer;
   ASSERT_TRUE(symbolizer.symbolize(a));
   EXPECT_EQ("folly::symbolizer::test::foo()", demangle(a.name.str().c_str()));