/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 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_
#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 folly {
namespace symbolizer {
+class Symbolizer;
+
/**
- * Address information: symbol name and location.
- *
- * Note that both name and location are references in the Symbolizer object,
- * which must outlive this AddressInfo object.
+ * Frame information: symbol name and location.
*/
-struct AddressInfo {
- /* implicit */ AddressInfo(uintptr_t a=0, bool sf=false)
- : address(a),
- isSignalFrame(sf),
- found(false) { }
- uintptr_t address;
+struct SymbolizedFrame {
+ SymbolizedFrame() : found(false), name(nullptr) { }
+
+ void set(const std::shared_ptr<ElfFile>& file, uintptr_t address);
+ void clear() { *this = SymbolizedFrame(); }
+
bool isSignalFrame;
bool found;
- StringPiece name;
+ const char* name;
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() : frameCount(0) { }
+
+ size_t frameCount;
+ uintptr_t addresses[N];
+ SymbolizedFrame 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.
+ * 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.
*/
-ssize_t getStackTrace(AddressInfo* addresses,
- size_t maxAddresses,
- size_t skip=0);
+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:
- Symbolizer() : fileCount_(0) { }
+ explicit Symbolizer(ElfCacheBase* cache = nullptr);
/**
* Symbolize given addresses.
*/
- void symbolize(AddressInfo* addresses, size_t addressCount);
+ void symbolize(const uintptr_t* addresses,
+ SymbolizedFrame* frames,
+ size_t frameCount);
+
+ template <size_t N>
+ void symbolize(FrameArray<N>& fa) {
+ symbolize(fa.addresses, fa.frames, fa.frameCount);
+ }
/**
* Shortcut to symbolize one address.
*/
- bool symbolize(AddressInfo& address) {
- symbolize(&address, 1);
- return address.found;
+ bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
+ symbolize(&address, &frame, 1);
+ return frame.found;
}
private:
- // We can't allocate memory, so we'll preallocate room.
- // "1023 shared libraries should be enough for everyone"
- static constexpr size_t kMaxFiles = 1024;
- size_t fileCount_;
- ElfFile files_[kMaxFiles];
+ ElfCacheBase* cache_;
+};
+
+/**
+ * Format one address in the way it's usually printer 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)];
};
/**
*/
class SymbolizePrinter {
public:
- void print(const AddressInfo& ainfo);
- void print(const AddressInfo* addresses, size_t addressCount);
+ /**
+ * 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);
+
+ /**
+ * 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,
+ };
+
+ enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE };
+ 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;
};
*/
class OStreamSymbolizePrinter : public SymbolizePrinter {
public:
- explicit OStreamSymbolizePrinter(std::ostream& out) : out_(out) { }
+ explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
private:
void doPrint(StringPiece sp) override;
std::ostream& out_;
*/
class FDSymbolizePrinter : public SymbolizePrinter {
public:
- explicit FDSymbolizePrinter(int fd) : fd_(fd) { }
+ explicit FDSymbolizePrinter(int fd, int options=0,
+ size_t bufferSize=0);
+ ~FDSymbolizePrinter();
+ void flush();
private:
void doPrint(StringPiece sp) override;
+
int fd_;
+ std::unique_ptr<IOBuf> buffer_;
};
/**
- * Print an AddressInfo to a stream. Note that the Symbolizer that
- * symbolized the address must outlive the AddressInfo. Just like
- * OStreamSymbolizePrinter (which it uses internally), this is not
- * reentrant; do not use from signal handling code.
+ * Print a list of symbolized addresses to a FILE*.
+ * Ignores errors. Not reentrant. Do not use from signal handling code.
*/
-std::ostream& operator<<(std::ostream& out, const AddressInfo& ainfo);
+class FILESymbolizePrinter : public SymbolizePrinter {
+ public:
+ explicit FILESymbolizePrinter(FILE* file, int options=0);
+ 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
#endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */
-