fall back to .debug_info scan in fatal signal handler v2016.10.17.00
authorPhilip Pronin <philipp@fb.com>
Sat, 15 Oct 2016 04:44:00 +0000 (21:44 -0700)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Sat, 15 Oct 2016 04:53:31 +0000 (21:53 -0700)
Summary:
We've found clang might be generating incomplete `.debug_aranges`,
while falling back to linear `.debug_info` scan is too expensive and shouldn't
be used by default, we can afford doing that in fatal signal handler.

Also optimize `exception_tracer::printExceptionInfo()` to avoid `LocationInfo`
resolution if `NO_FILE_AND_LINE` is used.

Reviewed By: luciang, ot

Differential Revision: D4020989

fbshipit-source-id: 84172208736b224c19206da48bcb3b5c0b2c67d0

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

index 1e3159a568fe795f188dce83cbe864f3d861de1f..d2ec8f64a6b2d0e22d93def0c413b569c134866f 100644 (file)
@@ -73,7 +73,10 @@ void printExceptionInfo(
       std::vector<SymbolizedFrame> frames;
       frames.resize(frameCount);
 
-      Symbolizer symbolizer;
+      Symbolizer symbolizer(
+          (options & SymbolizePrinter::NO_FILE_AND_LINE)
+              ? Dwarf::LocationInfoMode::DISABLED
+              : Symbolizer::kDefaultLocationInfoMode);
       symbolizer.symbolize(addresses, frames.data(), frameCount);
 
       OStreamSymbolizePrinter osp(out, options);
index 7709dc0bfa6e8c2e64c5127f9ac004590ece50d3..4474895f2335461bc0a66f67b5d6411ae57ca1b7 100644 (file)
@@ -566,10 +566,16 @@ bool Dwarf::findLocation(uintptr_t address,
   return locationInfo.hasFileAndLine;
 }
 
-bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
+bool Dwarf::findAddress(uintptr_t address,
+                        LocationInfo& locationInfo,
+                        LocationInfoMode mode) const {
   locationInfo = LocationInfo();
 
-  if (!elf_) { // no file
+  if (mode == LocationInfoMode::DISABLED) {
+    return false;
+  }
+
+  if (!elf_) { // No file.
     return false;
   }
 
@@ -577,20 +583,25 @@ bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
     // Fast path: find the right .debug_info entry by looking up the
     // address in .debug_aranges.
     uint64_t offset = 0;
-    if (!findDebugInfoOffset(address, aranges_, offset)) {
-      // NOTE: clang doesn't generate entries in .debug_aranges for
-      // some functions, but always generates .debug_info entries.
-      // We could read them from .debug_info but that's too slow.
-      // If .debug_aranges is present fast address lookup is assumed.
+    if (findDebugInfoOffset(address, aranges_, offset)) {
+      // Read compilation unit header from .debug_info
+      folly::StringPiece infoEntry(info_);
+      infoEntry.advance(offset);
+      findLocation(address, infoEntry, locationInfo);
+      return locationInfo.hasFileAndLine;
+    } else if (mode == LocationInfoMode::FAST) {
+      // NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries
+      // in .debug_aranges for some functions, but always generates
+      // .debug_info entries.  Scanning .debug_info is slow, so fall back to
+      // it only if such behavior is requested via LocationInfoMode.
       return false;
+    } else {
+      DCHECK(mode == LocationInfoMode::FULL);
+      // Fall back to the linear scan.
     }
-    // Read compilation unit header from .debug_info
-    folly::StringPiece infoEntry(info_);
-    infoEntry.advance(offset);
-    findLocation(address, infoEntry, locationInfo);
-    return true;
   }
 
+
   // Slow path (linear scan): Iterate over all .debug_info entries
   // and look for the address in each compilation unit.
   folly::StringPiece infoEntry(info_);
index ed7d1ae82cab9e4d662459ba29591dc0912bc1f1..192d0c0339fbbedb3a73c9be0e9e8c6085904a5d 100644 (file)
@@ -100,25 +100,37 @@ class Dwarf {
     folly::StringPiece file_;
   };
 
-  struct LocationInfo {
-    LocationInfo() : hasMainFile(false), hasFileAndLine(false), line(0) { }
+  enum class LocationInfoMode {
+    // Don't resolve location info.
+    DISABLED,
+    // Perform CU lookup using .debug_aranges (might be incomplete).
+    FAST,
+    // Scan all CU in .debug_info (slow!) on .debug_aranges lookup failure.
+    FULL,
+  };
 
-    bool hasMainFile;
+  struct LocationInfo {
+    bool hasMainFile = false;
     Path mainFile;
 
-    bool hasFileAndLine;
+    bool hasFileAndLine = false;
     Path file;
-    uint64_t line;
+    uint64_t line = 0;
   };
 
-  /** Find the file and line number information corresponding to address */
-  bool findAddress(uintptr_t address, LocationInfo& info) const;
+  /**
+   * Find the file and line number information corresponding to address.
+   */
+  bool findAddress(uintptr_t address,
+                   LocationInfo& info,
+                   LocationInfoMode mode) const;
 
  private:
-  void init();
   static bool findDebugInfoOffset(uintptr_t address,
                                   StringPiece aranges,
                                   uint64_t& offset);
+
+  void init();
   bool findLocation(uintptr_t address,
                     StringPiece& infoEntry,
                     LocationInfo& info) const;
index b5dd4c140f8ba102f10b9fa897cb3485c1b88856..06173c08f2be89d997f0e22fcc40fafe349158e6 100644 (file)
@@ -396,7 +396,9 @@ void dumpStackTrace(bool symbolize) {
   if (!getStackTraceSafe(addresses)) {
     print("(error retrieving stack trace)\n");
   } else if (symbolize) {
-    Symbolizer symbolizer(gSignalSafeElfCache);
+    // 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:
index 365aff41378d4a700bc0b7e04a506f6e8b3c850d..13fe68fa38d4ab054f7b039ccbd301a1353d0941 100644 (file)
@@ -60,7 +60,8 @@ ElfCache* defaultElfCache() {
 }  // namespace
 
 void SymbolizedFrame::set(const std::shared_ptr<ElfFile>& file,
-                          uintptr_t address) {
+                          uintptr_t address,
+                          Dwarf::LocationInfoMode mode) {
   clear();
   found = true;
 
@@ -73,12 +74,11 @@ void SymbolizedFrame::set(const std::shared_ptr<ElfFile>& file,
   file_ = file;
   name = file->getSymbolName(sym);
 
-  Dwarf(file.get()).findAddress(address, location);
+  Dwarf(file.get()).findAddress(address, location, mode);
 }
 
-
-Symbolizer::Symbolizer(ElfCacheBase* cache)
-  : cache_(cache ?: defaultElfCache()) {
+Symbolizer::Symbolizer(ElfCacheBase* cache, Dwarf::LocationInfoMode mode)
+  : cache_(cache ?: defaultElfCache()), mode_(mode) {
 }
 
 void Symbolizer::symbolize(const uintptr_t* addresses,
@@ -143,7 +143,7 @@ void Symbolizer::symbolize(const uintptr_t* addresses,
       auto const adjusted = addr - base;
 
       if (elfFile->getSectionContainingAddress(adjusted)) {
-        frame.set(elfFile, adjusted);
+        frame.set(elfFile, adjusted, mode_);
         --remaining;
       }
     }
index bbbb1e7588e43a3dddf902b7905c779fb853f476..bd93c121f42195fe62aa2057991cd951797a3659 100644 (file)
@@ -41,7 +41,10 @@ class Symbolizer;
 struct SymbolizedFrame {
   SymbolizedFrame() { }
 
-  void set(const std::shared_ptr<ElfFile>& file, uintptr_t address);
+  void set(const std::shared_ptr<ElfFile>& file,
+           uintptr_t address,
+           Dwarf::LocationInfoMode mode);
+
   void clear() { *this = SymbolizedFrame(); }
 
   bool found = false;
@@ -54,6 +57,7 @@ struct SymbolizedFrame {
   fbstring demangledName() const {
     return name ? demangle(name) : fbstring();
   }
+
  private:
   std::shared_ptr<ElfFile> file_;
 };
@@ -107,7 +111,14 @@ inline bool getStackTraceSafe(FrameArray<N>& fa) {
 
 class Symbolizer {
  public:
-  explicit Symbolizer(ElfCacheBase* cache = nullptr);
+  static constexpr Dwarf::LocationInfoMode kDefaultLocationInfoMode =
+      Dwarf::LocationInfoMode::FAST;
+
+  explicit Symbolizer(Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode)
+    : Symbolizer(nullptr, mode) {}
+
+  explicit Symbolizer(ElfCacheBase* cache,
+                      Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode);
 
   /**
    * Symbolize given addresses.
@@ -130,7 +141,8 @@ class Symbolizer {
   }
 
  private:
-  ElfCacheBase* const cache_ = nullptr;
+  ElfCacheBase* const cache_;
+  const Dwarf::LocationInfoMode mode_;
 };
 
 /**
index 877b58118014fbc40851761f4da0707860d79bfc..50f63c80f94d30f6d4a8a93e6b5ea25ee55dbc8c 100644 (file)
 
 void dummy() {}
 
-BENCHMARK(DwarfFindAddress, n) {
+namespace {
+
+using namespace folly::symbolizer;
+
+void run(Dwarf::LocationInfoMode mode, size_t n) {
   folly::BenchmarkSuspender suspender;
-  using namespace folly::symbolizer;
   // NOTE: Using '/proc/self/exe' only works if the code for @dummy is
   // statically linked into the binary.
   ElfFile elf("/proc/self/exe");
@@ -30,10 +33,20 @@ BENCHMARK(DwarfFindAddress, n) {
   suspender.dismiss();
   for (size_t i = 0; i < n; i++) {
     Dwarf::LocationInfo info;
-    dwarf.findAddress(uintptr_t(&dummy), info);
+    dwarf.findAddress(uintptr_t(&dummy), info, mode);
   }
 }
 
+} // namespace
+
+BENCHMARK(DwarfFindAddressFast, n) {
+  run(folly::symbolizer::Dwarf::LocationInfoMode::FAST, n);
+}
+
+BENCHMARK(DwarfFindAddressFull, n) {
+  run(folly::symbolizer::Dwarf::LocationInfoMode::FULL, n);
+}
+
 int main(int argc, char* argv[]) {
   gflags::ParseCommandLineFlags(&argc, &argv, true);
   google::InitGoogleLogging(argv[0]);