Switch to folly symbolizer
authorTudor Bosman <tudorb@fb.com>
Tue, 10 Dec 2013 03:57:56 +0000 (19:57 -0800)
committerJordan DeLong <jdelong@fb.com>
Fri, 20 Dec 2013 21:05:30 +0000 (13:05 -0800)
Summary:
(not committing yet, but I want to trigger unittests)

The glog symbolizer that we use has a few bugs (abort()s on certain small
shared libraries) and doesn't allow us to distinguish between template
specializations and function overloads (which, given that our code is more
template-heavy than Google's, has in fact been an issue).

Luckily, we have our own in folly, which doesn't have these problems and also
supports mappings from address to file and line number.

Switch translateFrames (aka the fb303 call that cpprof uses) to our symbolizer.
Also, removed a lot of dead code in common/process.

Test Plan: common/process, tested cpprof by hand

Reviewed By: lucian@fb.com

FB internal diff: D1090907

@override-unit-failures

folly/String.cpp
folly/experimental/exception_tracer/ExceptionTracer.cpp
folly/experimental/symbolizer/SignalHandler.cpp
folly/experimental/symbolizer/Symbolizer.cpp
folly/experimental/symbolizer/Symbolizer.h

index 44b6a783369ddfbdc0060841ea89226f4e35404d..a90dd2250527037a07e0d4cf6977c9857f61cd2d 100644 (file)
 // symbols" (but, interestingly enough, will resolve undefined weak symbols
 // with definitions from archive members that were extracted in order to
 // resolve an undefined global (strong) symbol)
+
+# ifndef DMGL_NO_OPTS
+#  define FOLLY_DEFINED_DMGL 1
+#  define DMGL_NO_OPTS    0          /* For readability... */
+#  define DMGL_PARAMS     (1 << 0)   /* Include function args */
+#  define DMGL_ANSI       (1 << 1)   /* Include const, volatile, etc */
+#  define DMGL_JAVA       (1 << 2)   /* Demangle as Java rather than C++. */
+#  define DMGL_VERBOSE    (1 << 3)   /* Include implementation details.  */
+#  define DMGL_TYPES      (1 << 4)   /* Also try to demangle type encodings.  */
+#  define DMGL_RET_POSTFIX (1 << 5)  /* Print function return types (when
+                                        present) after function signature */
+# endif
+
 extern "C" int cplus_demangle_v3_callback(
     const char* mangled,
     int options,  // We use DMGL_PARAMS | DMGL_TYPES, aka 0x11
@@ -340,7 +353,7 @@ size_t demangle(const char* name, char* out, size_t outSize) {
   // Unlike most library functions, this returns 1 on success and 0 on failure
   int status = cplus_demangle_v3_callback(
       name,
-      0x11,  // DMGL_PARAMS | DMGL_TYPES
+      DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES,
       demangleCallback,
       &dbuf);
   if (status == 0) {  // failed, return original
@@ -410,3 +423,15 @@ size_t hexDumpLine(const void* ptr, size_t offset, size_t size,
 } // namespace detail
 
 }   // namespace folly
+
+#ifdef FOLLY_DEFINED_DMGL
+# undef FOLLY_DEFINED_DMGL
+# undef DMGL_NO_OPTS
+# undef DMGL_PARAMS
+# undef DMGL_ANSI
+# undef DMGL_JAVA
+# undef DMGL_VERBOSE
+# undef DMGL_TYPES
+# undef DMGL_RET_POSTFIX
+#endif
+
index 533eb009a8567d69c4a7296b089207e608fab825..c2fd2b9f566d2d6df997c0e4f297324a7014c382 100644 (file)
@@ -70,7 +70,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
       symbolizer.symbolize(addresses, frames.data(), frameCount);
 
       OStreamSymbolizePrinter osp(out);
-      osp.print(addresses, frames.data(), frameCount);
+      osp.println(addresses, frames.data(), frameCount);
     }
   } catch (const std::exception& e) {
     out << "\n !! caught " << folly::exceptionStr(e) << "\n";
index 8cdf03a4847f43a0c06415db6bb75663c6b6b4aa..dc34c23557ac61dc7ae3e636e9296900bf188af6 100644 (file)
@@ -201,7 +201,7 @@ void dumpStackTrace() {
     symbolizer.symbolize(addresses);
 
     FDSymbolizePrinter printer(STDERR_FILENO);
-    printer.print(addresses);
+    printer.println(addresses);
   }
 }
 
index 563abb754e29a02bb94eb6c417509040e061e646..2719caaa54221c3f14860c47ca8815714782035e 100644 (file)
@@ -247,6 +247,11 @@ const char kHexChars[] = "0123456789abcdef";
 }  // namespace
 
 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
+  if (options_ & TERSE) {
+    printTerse(address, frame);
+    return;
+  }
+
   // Can't use sprintf, not async-signal-safe
   static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
   char buf[] = "    @ 0000000000000000";
@@ -273,7 +278,6 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
   } else if (frame.name.size() >= sizeof(mangledBuf)) {
     doPrint(" ");
     doPrint(frame.name);
-    doPrint("\n");
   } else {
     memcpy(mangledBuf, frame.name.data(), frame.name.size());
     mangledBuf[frame.name.size()] = '\0';
@@ -282,41 +286,73 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
     demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
     doPrint(" ");
     doPrint(demangledBuf);
-    doPrint("\n");
   }
 
-  char fileBuf[PATH_MAX];
-  fileBuf[0] = '\0';
-  if (frame.location.hasFileAndLine) {
-    frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
-    doPrint(pad);
-    doPrint(fileBuf);
-
-    char buf[22];
-    uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
-    doPrint(":");
-    doPrint(StringPiece(buf, n));
-    doPrint("\n");
+  if (!(options_ & NO_FILE_AND_LINE)) {
+    char fileBuf[PATH_MAX];
+    fileBuf[0] = '\0';
+    if (frame.location.hasFileAndLine) {
+      frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
+      doPrint("\n");
+      doPrint(pad);
+      doPrint(fileBuf);
+
+      char buf[22];
+      uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
+      doPrint(":");
+      doPrint(StringPiece(buf, n));
+    }
+
+    if (frame.location.hasMainFile) {
+      char mainFileBuf[PATH_MAX];
+      mainFileBuf[0] = '\0';
+      frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
+      if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
+        doPrint("\n");
+        doPrint(pad);
+        doPrint("-> ");
+        doPrint(mainFileBuf);
+      }
+    }
   }
+}
 
-  if (frame.location.hasMainFile) {
-    char mainFileBuf[PATH_MAX];
-    mainFileBuf[0] = '\0';
-    frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
-    if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
-      doPrint(pad);
-      doPrint("-> ");
-      doPrint(mainFileBuf);
-      doPrint("\n");
+void SymbolizePrinter::println(uintptr_t address,
+                               const SymbolizedFrame& frame) {
+  print(address, frame);
+  doPrint("\n");
+}
+
+void SymbolizePrinter::printTerse(uintptr_t address,
+                                  const SymbolizedFrame& frame) {
+  if (frame.found) {
+    char mangledBuf[1024];
+    memcpy(mangledBuf, frame.name.data(), frame.name.size());
+    mangledBuf[frame.name.size()] = '\0';
+
+    char demangledBuf[1024];
+    demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
+    doPrint(demangledBuf);
+  } else {
+    // Can't use sprintf, not async-signal-safe
+    static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
+    char buf[] = "0x0000000000000000";
+    char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
+    char* p = end;
+    *p-- = '\0';
+    while (address != 0) {
+      *p-- = kHexChars[address & 0xf];
+      address >>= 4;
     }
+    doPrint(StringPiece(buf, end));
   }
 }
 
-void SymbolizePrinter::print(const uintptr_t* addresses,
-                             const SymbolizedFrame* frames,
-                             size_t frameCount) {
+void SymbolizePrinter::println(const uintptr_t* addresses,
+                               const SymbolizedFrame* frames,
+                               size_t frameCount) {
   for (size_t i = 0; i < frameCount; ++i) {
-    print(addresses[i], frames[i]);
+    println(addresses[i], frames[i]);
   }
 }
 
@@ -328,5 +364,13 @@ void FDSymbolizePrinter::doPrint(StringPiece sp) {
   writeFull(fd_, sp.data(), sp.size());
 }
 
+void FILESymbolizePrinter::doPrint(StringPiece sp) {
+  fwrite(sp.data(), 1, sp.size(), file_);
+}
+
+void StringSymbolizePrinter::doPrint(StringPiece sp) {
+  buf_.append(sp.data(), sp.size());
+}
+
 }  // namespace symbolizer
 }  // namespace folly
index d3e6d4525b996079807234040500de6cdcc2e1a1..bef46f3bbfb14e0ad68ae3532867f836ac2dabac 100644 (file)
@@ -22,6 +22,7 @@
 #include <string>
 #include <unordered_map>
 
+#include "folly/FBString.h"
 #include "folly/Range.h"
 #include "folly/experimental/symbolizer/Elf.h"
 #include "folly/experimental/symbolizer/Dwarf.h"
@@ -110,20 +111,50 @@ class Symbolizer {
  */
 class SymbolizePrinter {
  public:
+  /**
+   * Print one address, no ending newline.
+   */
   void print(uintptr_t address, const SymbolizedFrame& frame);
-  void print(const uintptr_t* addresses,
-             const SymbolizedFrame* frames,
-             size_t frameCount);
 
+  /**
+   * Print one address with ending newline.
+   */
+  void println(uintptr_t address, const SymbolizedFrame& frame);
+
+  /**
+   * Print multiple addresses on separate lines.
+   */
+  void println(const uintptr_t* addresses,
+               const SymbolizedFrame* frames,
+               size_t frameCount);
+
+  /**
+   * Print multiple addresses on separate lines, skipping the first
+   * skip addresses.
+   */
   template <size_t N>
-  void print(const FrameArray<N>& fa, size_t skip=0) {
+  void println(const FrameArray<N>& fa, size_t skip=0) {
     if (skip < fa.frameCount) {
-      print(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
+      println(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
     }
   }
 
   virtual ~SymbolizePrinter() { }
+
+  enum Options {
+    // Skip file and line information
+    NO_FILE_AND_LINE = 1 << 0,
+
+    // As terse as it gets: function name if found, address otherwise
+    TERSE = 1 << 1,
+  };
+
+ protected:
+  explicit SymbolizePrinter(int options) : options_(options) { }
+  const int options_;
+
  private:
+  void printTerse(uintptr_t address, const SymbolizedFrame& frame);
   virtual void doPrint(StringPiece sp) = 0;
 };
 
@@ -133,7 +164,9 @@ class SymbolizePrinter {
  */
 class OStreamSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit OStreamSymbolizePrinter(std::ostream& out) : out_(out) { }
+  explicit OStreamSymbolizePrinter(std::ostream& out, int options=0)
+    : SymbolizePrinter(options),
+      out_(out) { }
  private:
   void doPrint(StringPiece sp) override;
   std::ostream& out_;
@@ -145,12 +178,45 @@ class OStreamSymbolizePrinter : public SymbolizePrinter {
  */
 class FDSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit FDSymbolizePrinter(int fd) : fd_(fd) { }
+  explicit FDSymbolizePrinter(int fd, int options=0)
+    : SymbolizePrinter(options),
+      fd_(fd) { }
  private:
   void doPrint(StringPiece sp) override;
   int fd_;
 };
 
+/**
+ * Print a list of symbolized addresses to a FILE*.
+ * Ignores errors. Not reentrant. Do not use from signal handling code.
+ */
+class FILESymbolizePrinter : public SymbolizePrinter {
+ public:
+  explicit FILESymbolizePrinter(FILE* file, int options=0)
+    : SymbolizePrinter(options),
+      file_(file) { }
+ private:
+  void doPrint(StringPiece sp) override;
+  FILE* file_;
+};
+
+/**
+ * Print a list of symbolized addresses to a std::string.
+ * Not reentrant. Do not use from signal handling code.
+ */
+class StringSymbolizePrinter : public SymbolizePrinter {
+ public:
+  explicit StringSymbolizePrinter(int options=0) : SymbolizePrinter(options) { }
+
+  std::string str() const { return buf_.toStdString(); }
+  const fbstring& fbstr() const { return buf_; }
+  fbstring moveFbString() { return std::move(buf_); }
+
+ private:
+  void doPrint(StringPiece sp) override;
+  fbstring buf_;
+};
+
 }  // namespace symbolizer
 }  // namespace folly