fall back to .debug_info scan in fatal signal handler
[folly.git] / folly / experimental / symbolizer / Symbolizer.h
index 0b38a5db406888a5859fb446db7256b6de2e9de5..bd93c121f42195fe62aa2057991cd951797a3659 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
+#pragma once
 
-#ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
-#define FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
-
+#include <array>
 #include <cstdint>
+#include <memory>
 #include <string>
-#include <unordered_map>
 
-#include "folly/Range.h"
-#include "folly/experimental/symbolizer/Elf.h"
-#include "folly/experimental/symbolizer/Dwarf.h"
+#include <folly/FBString.h>
+#include <folly/Range.h>
+#include <folly/String.h>
+#include <folly/io/IOBuf.h>
+#include <folly/experimental/symbolizer/Elf.h>
+#include <folly/experimental/symbolizer/ElfCache.h>
+#include <folly/experimental/symbolizer/Dwarf.h>
+#include <folly/experimental/symbolizer/StackTrace.h>
 
-namespace facebook {
+namespace folly {
 namespace symbolizer {
 
+class Symbolizer;
+
+/**
+ * Frame information: symbol name and location.
+ */
+struct SymbolizedFrame {
+  SymbolizedFrame() { }
+
+  void set(const std::shared_ptr<ElfFile>& file,
+           uintptr_t address,
+           Dwarf::LocationInfoMode mode);
+
+  void clear() { *this = SymbolizedFrame(); }
+
+  bool found = false;
+  const char* name = nullptr;
+  Dwarf::LocationInfo location;
+
+  /**
+   * Demangle the name and return it. Not async-signal-safe; allocates memory.
+   */
+  fbstring demangledName() const {
+    return name ? demangle(name) : fbstring();
+  }
+
+ private:
+  std::shared_ptr<ElfFile> file_;
+};
+
+template <size_t N>
+struct FrameArray {
+  FrameArray() { }
+
+  size_t frameCount = 0;
+  uintptr_t addresses[N];
+  SymbolizedFrame frames[N];
+};
+
 /**
- * Convert an address to symbol name and source location.
+ * 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.
  */
+namespace detail {
+template <size_t N>
+bool fixFrameArray(FrameArray<N>& fa, ssize_t n) {
+  if (n != -1) {
+    fa.frameCount = n;
+    for (size_t i = 0; i < fa.frameCount; ++i) {
+      fa.frames[i].found = false;
+    }
+    return true;
+  } else {
+    fa.frameCount = 0;
+    return false;
+  }
+}
+}  // namespace detail
+
+// Always inline these functions; they don't do much, and unittests rely
+// on them never showing up in a stack trace.
+template <size_t N>
+FOLLY_ALWAYS_INLINE bool getStackTrace(FrameArray<N>& fa);
+
+template <size_t N>
+inline bool getStackTrace(FrameArray<N>& fa) {
+  return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));
+}
+template <size_t N>
+FOLLY_ALWAYS_INLINE bool getStackTraceSafe(FrameArray<N>& fa);
+
+template <size_t N>
+inline bool getStackTraceSafe(FrameArray<N>& fa) {
+  return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
+}
+
 class Symbolizer {
  public:
+  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 an instruction pointer address, returning the symbol name
-   * and file/line number information.
-   *
-   * The returned StringPiece objects are valid for the lifetime of
-   * this Symbolizer object.
+   * Symbolize given addresses.
    */
-  bool symbolize(uintptr_t address, folly::StringPiece& symbolName,
-                 Dwarf::LocationInfo& location);
+  void symbolize(const uintptr_t* addresses,
+                 SymbolizedFrame* frames,
+                 size_t frameCount);
 
-  static void write(std::ostream& out, uintptr_t address,
-                    folly::StringPiece symbolName,
-                    const Dwarf::LocationInfo& location);
+  template <size_t N>
+  void symbolize(FrameArray<N>& fa) {
+    symbolize(fa.addresses, fa.frames, fa.frameCount);
+  }
+
+  /**
+   * Shortcut to symbolize one address.
+   */
+  bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
+    symbolize(&address, &frame, 1);
+    return frame.found;
+  }
 
  private:
-  ElfFile& getFile(const std::string& name);
-  // cache open ELF files
-  std::unordered_map<std::string, ElfFile> elfFiles_;
+  ElfCacheBase* const cache_;
+  const Dwarf::LocationInfoMode mode_;
 };
 
-}  // namespace symbolizer
-}  // namespace facebook
+/**
+ * Format one address in the way it's usually printed by SymbolizePrinter.
+ * Async-signal-safe.
+ */
+class AddressFormatter {
+ public:
+  AddressFormatter();
+
+  /**
+   * Format the address. Returns an internal buffer.
+   */
+  StringPiece format(uintptr_t address);
+
+ private:
+  static constexpr char bufTemplate[] = "    @ 0000000000000000";
+  char buf_[sizeof(bufTemplate)];
+};
+
+/**
+ * Print a list of symbolized addresses. Base class.
+ */
+class SymbolizePrinter {
+ public:
+  /**
+   * Print one address, no ending newline.
+   */
+  void print(uintptr_t address, const SymbolizedFrame& frame);
+
+  /**
+   * Print one address with ending newline.
+   */
+  void println(uintptr_t address, const SymbolizedFrame& frame);
 
-#endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */
+  /**
+   * Print multiple addresses on separate lines.
+   */
+  void println(const uintptr_t* addresses,
+               const SymbolizedFrame* frames,
+               size_t frameCount);
 
+  /**
+   * Print a string, no endling newline.
+   */
+  void print(StringPiece sp) { doPrint(sp); }
+
+  /**
+   * Print multiple addresses on separate lines, skipping the first
+   * skip addresses.
+   */
+  template <size_t N>
+  void println(const FrameArray<N>& fa, size_t skip=0) {
+    if (skip < fa.frameCount) {
+      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,
+
+    // Always colorize output (ANSI escape code)
+    COLOR = 1 << 2,
+
+    // Colorize output only if output is printed to a TTY (ANSI escape code)
+    COLOR_IF_TTY = 1 << 3,
+
+    // Skip frame address information
+    NO_FRAME_ADDRESS = 1 << 4,
+  };
+
+  // NOTE: enum values used as indexes in kColorMap.
+  enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE, NUM };
+  void color(Color c);
+
+ protected:
+  explicit SymbolizePrinter(int options, bool isTty = false)
+    : options_(options),
+      isTty_(isTty) {
+  }
+
+  const int options_;
+  const bool isTty_;
+
+ private:
+  void printTerse(uintptr_t address, const SymbolizedFrame& frame);
+  virtual void doPrint(StringPiece sp) = 0;
+
+  static constexpr std::array<const char*, Color::NUM> kColorMap = {{
+      "\x1B[0m",
+      "\x1B[31m",
+      "\x1B[32m",
+      "\x1B[33m",
+      "\x1B[34m",
+      "\x1B[36m",
+      "\x1B[37m",
+      "\x1B[35m",
+  }};
+};
+
+/**
+ * Print a list of symbolized addresses to a stream.
+ * Not reentrant. Do not use from signal handling code.
+ */
+class OStreamSymbolizePrinter : public SymbolizePrinter {
+ public:
+  explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
+ private:
+  void doPrint(StringPiece sp) override;
+  std::ostream& out_;
+};
+
+/**
+ * Print a list of symbolized addresses to a file descriptor.
+ * Ignores errors. Async-signal-safe.
+ */
+class FDSymbolizePrinter : public SymbolizePrinter {
+ public:
+  explicit FDSymbolizePrinter(int fd, int options=0,
+                              size_t bufferSize=0);
+  ~FDSymbolizePrinter();
+  void flush();
+ private:
+  void doPrint(StringPiece sp) override;
+
+  const int fd_;
+  std::unique_ptr<IOBuf> buffer_;
+};
+
+/**
+ * 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);
+ private:
+  void doPrint(StringPiece sp) override;
+  FILE* const file_ = nullptr;
+};
+
+/**
+ * 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