123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- /*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #pragma once
- #include <array>
- #include <cstdint>
- #include <memory>
- #include <string>
- #include <folly/FBString.h>
- #include <folly/Optional.h>
- #include <folly/Range.h>
- #include <folly/String.h>
- #include <folly/Synchronized.h>
- #include <folly/container/EvictingCacheMap.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.
- */
- 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];
- };
- /**
- * 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,
- size_t symbolCacheSize = 0);
- /**
- * Symbolize given addresses.
- */
- 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(uintptr_t address, SymbolizedFrame& frame) {
- symbolize(&address, &frame, 1);
- return frame.found;
- }
- private:
- ElfCacheBase* const cache_;
- const Dwarf::LocationInfoMode mode_;
- using SymbolCache = EvictingCacheMap<uintptr_t, SymbolizedFrame>;
- folly::Optional<Synchronized<SymbolCache>> symbolCache_;
- };
- /**
- * 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);
- /**
- * 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);
- }
- }
- /**
- * If output buffered inside this class, send it to the output stream, so that
- * any output done in other ways appears after this.
- */
- virtual void flush() {}
- 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() override;
- virtual void flush() override;
- 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_;
- };
- /**
- * 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 constructor 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 SafeStackTracePrinter {
- public:
- static constexpr size_t kDefaultMinSignalSafeElfCacheSize = 500;
- explicit SafeStackTracePrinter(
- size_t minSignalSafeElfCacheSize = kDefaultMinSignalSafeElfCacheSize,
- int fd = STDERR_FILENO);
- virtual ~SafeStackTracePrinter() {}
- /**
- * 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.
- *
- * This is NOINLINE to make sure it shows up in the stack we grab, which makes
- * it easy to skip printing it.
- */
- 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();
- protected:
- virtual void printSymbolizedStackTrace();
- private:
- static constexpr size_t kMaxStackTraceDepth = 100;
- int fd_;
- SignalSafeElfCache elfCache_;
- FDSymbolizePrinter printer_;
- std::unique_ptr<FrameArray<kMaxStackTraceDepth>> addresses_;
- };
- /**
- * Use this class to print a stack trace from normal code. It will malloc and
- * won't flush or sync.
- *
- * These methods are thread safe, through locking. However, they are not signal
- * safe.
- */
- class FastStackTracePrinter {
- public:
- static constexpr size_t kDefaultSymbolCacheSize = 10000;
- explicit FastStackTracePrinter(
- std::unique_ptr<SymbolizePrinter> printer,
- size_t elfCacheSize = 0, // 0 means "use the default elf cache instance."
- size_t symbolCacheSize = kDefaultSymbolCacheSize);
- ~FastStackTracePrinter();
- /**
- * This is NOINLINE to make sure it shows up in the stack we grab, which makes
- * it easy to skip printing it.
- */
- FOLLY_NOINLINE void printStackTrace(bool symbolize);
- void flush();
- private:
- static constexpr size_t kMaxStackTraceDepth = 100;
- const std::unique_ptr<ElfCache> elfCache_;
- const std::unique_ptr<SymbolizePrinter> printer_;
- Symbolizer symbolizer_;
- };
- /**
- * Use this class in rare situations where signal handlers are running in a
- * tiny stack specified by sigaltstack.
- *
- * This is neither thread-safe nor signal-safe. However, it can usually print
- * something useful while SafeStackTracePrinter would stack overflow.
- *
- * Signal handlers would need to block other signals to make this safer.
- * Note it's still unsafe even with that.
- */
- class UnsafeSelfAllocateStackTracePrinter : public SafeStackTracePrinter {
- protected:
- void printSymbolizedStackTrace() override;
- const long pageSizeUnchecked_ = sysconf(_SC_PAGESIZE);
- };
- } // namespace symbolizer
- } // namespace folly
|