Fix some copyright lines in folly/experimental/symbolizer/
[folly.git] / folly / experimental / symbolizer / Symbolizer.h
index 5595a53b67994270c9588de1561899ee7b41db1f..2c906b6e39bdbec036a19c00705e5f8a1530ea22 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2012-present 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.
  */
 
-#ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
-#define FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
+#pragma once
 
+#include <array>
 #include <cstdint>
+#include <memory>
 #include <string>
-#include <unordered_map>
 
-#include "folly/FBString.h"
-#include "folly/Range.h"
-#include "folly/String.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"
+#include <folly/FBString.h>
+#include <folly/Range.h>
+#include <folly/String.h>
+#include <folly/experimental/symbolizer/Dwarf.h>
+#include <folly/experimental/symbolizer/Elf.h>
+#include <folly/experimental/symbolizer/ElfCache.h>
+#include <folly/experimental/symbolizer/StackTrace.h>
+#include <folly/io/IOBuf.h>
 
 namespace folly {
 namespace symbolizer {
 
+class Symbolizer;
+
 /**
  * Frame information: symbol name and location.
- *
- * Note that both name and location are references in the Symbolizer object,
- * which must outlive this SymbolizedFrame object.
  */
 struct SymbolizedFrame {
-  SymbolizedFrame() : found(false) { }
-  bool isSignalFrame;
-  bool found;
-  StringPiece name;
+  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 demangle(name.fbstr().c_str());
+    return name ? demangle(name) : fbstring();
   }
+
+ private:
+  std::shared_ptr<ElfFile> file_;
 };
 
 template <size_t N>
 struct FrameArray {
-  FrameArray() : frameCount(0) { }
+  FrameArray() {}
 
-  size_t frameCount;
+  size_t frameCount = 0;
   uintptr_t addresses[N];
   SymbolizedFrame frames[N];
 };
@@ -81,19 +93,19 @@ bool fixFrameArray(FrameArray<N>& fa, ssize_t n) {
     return false;
   }
 }
-}  // namespace detail
+} // 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>
-inline bool getStackTrace(FrameArray<N>& fa) __attribute__((always_inline));
+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>
-inline bool getStackTraceSafe(FrameArray<N>& fa) __attribute__((always_inline));
+FOLLY_ALWAYS_INLINE bool getStackTraceSafe(FrameArray<N>& fa);
 
 template <size_t N>
 inline bool getStackTraceSafe(FrameArray<N>& fa) {
@@ -102,14 +114,23 @@ 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.
    */
-  void symbolize(const uintptr_t* addresses,
-                 SymbolizedFrame* frames,
-                 size_t frameCount);
+  void symbolize(
+      const uintptr_t* addresses,
+      SymbolizedFrame* frames,
+      size_t frameCount);
 
   template <size_t N>
   void symbolize(FrameArray<N>& fa) {
@@ -125,7 +146,26 @@ class Symbolizer {
   }
 
  private:
-  ElfCacheBase* cache_;
+  ElfCacheBase* const cache_;
+  const Dwarf::LocationInfoMode mode_;
+};
+
+/**
+ * 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)];
 };
 
 /**
@@ -146,22 +186,30 @@ class SymbolizePrinter {
   /**
    * Print multiple addresses on separate lines.
    */
-  void println(const uintptr_t* addresses,
-               const SymbolizedFrame* frames,
-               size_t frameCount);
+  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) {
+  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() { }
+  virtual ~SymbolizePrinter() {}
 
   enum Options {
     // Skip file and line information
@@ -175,16 +223,18 @@ class SymbolizePrinter {
 
     // 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,
   };
 
-  enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE };
+  // 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) {
-  }
+      : options_(options), isTty_(isTty) {}
 
   const int options_;
   const bool isTty_;
@@ -192,6 +242,17 @@ class SymbolizePrinter {
  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",
+  }};
 };
 
 /**
@@ -200,7 +261,8 @@ class SymbolizePrinter {
  */
 class OStreamSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
+  explicit OStreamSymbolizePrinter(std::ostream& out, int options = 0);
+
  private:
   void doPrint(StringPiece sp) override;
   std::ostream& out_;
@@ -212,10 +274,15 @@ class OStreamSymbolizePrinter : public SymbolizePrinter {
  */
 class FDSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit FDSymbolizePrinter(int fd, int options=0);
+  explicit FDSymbolizePrinter(int fd, int options = 0, size_t bufferSize = 0);
+  ~FDSymbolizePrinter() override;
+  void flush();
+
  private:
   void doPrint(StringPiece sp) override;
-  int fd_;
+
+  const int fd_;
+  std::unique_ptr<IOBuf> buffer_;
 };
 
 /**
@@ -224,10 +291,11 @@ class FDSymbolizePrinter : public SymbolizePrinter {
  */
 class FILESymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit FILESymbolizePrinter(FILE* file, int options=0);
+  explicit FILESymbolizePrinter(FILE* file, int options = 0);
+
  private:
   void doPrint(StringPiece sp) override;
-  FILE* file_;
+  FILE* const file_ = nullptr;
 };
 
 /**
@@ -236,18 +304,68 @@ class FILESymbolizePrinter : public SymbolizePrinter {
  */
 class StringSymbolizePrinter : public SymbolizePrinter {
  public:
-  explicit StringSymbolizePrinter(int options=0) : SymbolizePrinter(options) { }
+  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_); }
+  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
+/**
+ * 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:
+  static constexpr size_t kMaxStackTraceDepth = 100;
+
+  int fd_;
+  SignalSafeElfCache elfCache_;
+  FDSymbolizePrinter printer_;
+  std::unique_ptr<FrameArray<kMaxStackTraceDepth>> addresses_;
+};
 
-#endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */
+} // namespace symbolizer
+} // namespace folly