From: Tudor Bosman Date: Tue, 3 Dec 2013 23:11:11 +0000 (-0800) Subject: Print (2 more) if stack trace truncated X-Git-Tag: v0.22.0~770 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=730a0da803ab68553e7f7505dea5db6331389485 Print (2 more) if stack trace truncated Summary: Also, C++ify the interface and switch to per-thread caching in libunwind as per D1081259 Test Plan: folly/experimental/symbolizer/test Reviewed By: lucian@fb.com FB internal diff: D1081272 --- diff --git a/folly/experimental/exception_tracer/ExceptionTracer.cpp b/folly/experimental/exception_tracer/ExceptionTracer.cpp index 19b85ec2..2e84227a 100644 --- a/folly/experimental/exception_tracer/ExceptionTracer.cpp +++ b/folly/experimental/exception_tracer/ExceptionTracer.cpp @@ -54,7 +54,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) { << (info.frames.size() == 1 ? " frame" : " frames") << ")\n"; try { - std::vector addresses; + std::vector addresses; addresses.reserve(info.frames.size()); for (auto ip : info.frames) { // Symbolize the previous address because the IP might be in the @@ -66,7 +66,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) { symbolizer.symbolize(addresses.data(), addresses.size()); OStreamSymbolizePrinter osp(out); - osp.print(addresses.data(), addresses.size()); + osp.print(addresses.data(), addresses.size(), addresses.size()); } catch (const std::exception& e) { out << "\n !! caught " << folly::exceptionStr(e) << "\n"; } catch (...) { diff --git a/folly/experimental/symbolizer/SignalHandler.cpp b/folly/experimental/symbolizer/SignalHandler.cpp index f66d0133..58e8b184 100644 --- a/folly/experimental/symbolizer/SignalHandler.cpp +++ b/folly/experimental/symbolizer/SignalHandler.cpp @@ -193,18 +193,17 @@ void dumpStackTrace() { SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); }; // Get and symbolize stack trace constexpr size_t kMaxStackTraceDepth = 100; - AddressInfo addresses[kMaxStackTraceDepth]; + FrameArray addresses; // Skip the getStackTrace frame - ssize_t stackTraceDepth = getStackTrace(addresses, kMaxStackTraceDepth, 1); - if (stackTraceDepth < 0) { + if (!getStackTrace(addresses)) { print("(error retrieving stack trace)\n"); } else { Symbolizer symbolizer; - symbolizer.symbolize(addresses, stackTraceDepth); + symbolizer.symbolize(addresses); FDSymbolizePrinter printer(STDERR_FILENO); - printer.print(addresses, stackTraceDepth); + printer.print(addresses); } } diff --git a/folly/experimental/symbolizer/Symbolizer.cpp b/folly/experimental/symbolizer/Symbolizer.cpp index 79345c8b..f7e0d59d 100644 --- a/folly/experimental/symbolizer/Symbolizer.cpp +++ b/folly/experimental/symbolizer/Symbolizer.cpp @@ -148,7 +148,7 @@ bool parseProcMapsLine(StringPiece line, } // namespace -ssize_t getStackTrace(AddressInfo* addresses, +ssize_t getStackTrace(FrameInfo* addresses, size_t maxAddresses, size_t skip) { unw_context_t uctx; @@ -160,7 +160,7 @@ ssize_t getStackTrace(AddressInfo* addresses, unw_cursor_t cursor; size_t idx = 0; bool first = true; - while (idx < maxAddresses) { + for (;;) { if (first) { first = false; r = unw_init_local(&cursor, &uctx); @@ -178,26 +178,25 @@ ssize_t getStackTrace(AddressInfo* addresses, --skip; continue; } - unw_word_t ip; - int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip); - if (rr < 0) { - return -1; - } - - // If error, assume not a signal frame - rr = unw_is_signal_frame(&cursor); - addresses[idx++] = AddressInfo(ip, (rr > 0)); - } + if (idx < maxAddresses) { + unw_word_t ip; + int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip); + if (rr < 0) { + return -1; + } - if (r < 0) { - return -1; + // If error, assume not a signal frame + rr = unw_is_signal_frame(&cursor); + addresses[idx] = FrameInfo(ip, (rr > 0)); + } + ++idx; } return idx; } -void Symbolizer::symbolize(AddressInfo* addresses, size_t addressCount) { +void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) { size_t remaining = 0; for (size_t i = 0; i < addressCount; ++i) { auto& ainfo = addresses[i]; @@ -305,7 +304,7 @@ namespace { const char kHexChars[] = "0123456789abcdef"; } // namespace -void SymbolizePrinter::print(const AddressInfo& ainfo) { +void SymbolizePrinter::print(const FrameInfo& ainfo) { uintptr_t address = ainfo.address; // Can't use sprintf, not async-signal-safe static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?"); @@ -372,12 +371,22 @@ void SymbolizePrinter::print(const AddressInfo& ainfo) { } } -void SymbolizePrinter::print(const AddressInfo* addresses, - size_t addressCount) { - for (size_t i = 0; i < addressCount; ++i) { +void SymbolizePrinter::print(const FrameInfo* addresses, + size_t addressesSize, + size_t frameCount) { + for (size_t i = 0; i < std::min(addressesSize, frameCount); ++i) { auto& ainfo = addresses[i]; print(ainfo); } + + // Indicate the number of frames that we couldn't log due to space + if (frameCount > addressesSize) { + char buf[22]; + uint32_t n = uint64ToBufferUnsafe(frameCount - addressesSize, buf); + doPrint(" ("); + doPrint(StringPiece(buf, n)); + doPrint(" omitted, max buffer size reached)\n"); + } } void OStreamSymbolizePrinter::doPrint(StringPiece sp) { @@ -388,11 +397,29 @@ void FDSymbolizePrinter::doPrint(StringPiece sp) { writeFull(fd_, sp.data(), sp.size()); } -std::ostream& operator<<(std::ostream& out, const AddressInfo& ainfo) { +std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo) { OStreamSymbolizePrinter osp(out); osp.print(ainfo); return out; } +namespace { + +struct Init { + Init(); +}; + +Init::Init() { + // Don't use global caching -- it's slow and leads to lock contention. (And + // it's made signal-safe using sigprocmask to block all signals while the + // lock is being held, and sigprocmask contends on a lock inside the kernel, + // too, ugh.) + unw_set_caching_policy(unw_local_addr_space, UNW_CACHE_PER_THREAD); +} + +Init initializer; + +} // namespace + } // namespace symbolizer } // namespace folly diff --git a/folly/experimental/symbolizer/Symbolizer.h b/folly/experimental/symbolizer/Symbolizer.h index 307ca252..84913526 100644 --- a/folly/experimental/symbolizer/Symbolizer.h +++ b/folly/experimental/symbolizer/Symbolizer.h @@ -30,13 +30,13 @@ namespace folly { namespace symbolizer { /** - * Address information: symbol name and location. + * Frame information: symbol name and location. * * Note that both name and location are references in the Symbolizer object, - * which must outlive this AddressInfo object. + * which must outlive this FrameInfo object. */ -struct AddressInfo { - /* implicit */ AddressInfo(uintptr_t a=0, bool sf=false) +struct FrameInfo { + /* implicit */ FrameInfo(uintptr_t a=0, bool sf=false) : address(a), isSignalFrame(sf), found(false) { } @@ -47,15 +47,46 @@ struct AddressInfo { Dwarf::LocationInfo location; }; +template +struct FrameArray { + FrameArray() : frameCount(0) { } + + size_t frameCount; + FrameInfo 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. + * maxAddresses frames. Skip the first (topmost) skip entries. + * + * Returns the number of frames in the stack trace. Just like snprintf, + * if the number of frames is greater than maxAddresses, it will return + * the actual number of frames, so the stack trace was truncated iff + * the return value > maxAddresses. + * + * Returns -1 on failure. */ -ssize_t getStackTrace(AddressInfo* addresses, +ssize_t getStackTrace(FrameInfo* addresses, size_t maxAddresses, size_t skip=0); +/** + * 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. + */ +template +bool getStackTrace(FrameArray& fa, size_t skip=0) { + ssize_t n = getStackTrace(fa.frames, N, skip); + if (n != -1) { + fa.frameCount = n; + return true; + } else { + fa.frameCount = 0; + return false; + } +} + class Symbolizer { public: Symbolizer() : fileCount_(0) { } @@ -63,12 +94,17 @@ class Symbolizer { /** * Symbolize given addresses. */ - void symbolize(AddressInfo* addresses, size_t addressCount); + void symbolize(FrameInfo* addresses, size_t addressCount); + + template + void symbolize(FrameArray& fa) { + symbolize(fa.frames, std::min(fa.frameCount, N)); + } /** * Shortcut to symbolize one address. */ - bool symbolize(AddressInfo& address) { + bool symbolize(FrameInfo& address) { symbolize(&address, 1); return address.found; } @@ -86,8 +122,15 @@ class Symbolizer { */ class SymbolizePrinter { public: - void print(const AddressInfo& ainfo); - void print(const AddressInfo* addresses, size_t addressCount); + void print(const FrameInfo& ainfo); + void print(const FrameInfo* addresses, + size_t addressesSize, + size_t frameCount); + + template + void print(const FrameArray& fa) { + print(fa.frames, N, fa.frameCount); + } virtual ~SymbolizePrinter() { } private: @@ -119,12 +162,12 @@ class FDSymbolizePrinter : public SymbolizePrinter { }; /** - * Print an AddressInfo to a stream. Note that the Symbolizer that - * symbolized the address must outlive the AddressInfo. Just like + * Print an FrameInfo to a stream. Note that the Symbolizer that + * symbolized the address must outlive the FrameInfo. Just like * OStreamSymbolizePrinter (which it uses internally), this is not * reentrant; do not use from signal handling code. */ -std::ostream& operator<<(std::ostream& out, const AddressInfo& ainfo); +std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo); } // namespace symbolizer } // namespace folly diff --git a/folly/experimental/symbolizer/test/SymbolizerTest.cpp b/folly/experimental/symbolizer/test/SymbolizerTest.cpp index 0ff9cfde..e9a09674 100644 --- a/folly/experimental/symbolizer/test/SymbolizerTest.cpp +++ b/folly/experimental/symbolizer/test/SymbolizerTest.cpp @@ -27,7 +27,7 @@ void foo() { } TEST(Symbolizer, Single) { - AddressInfo a(reinterpret_cast(foo)); + FrameInfo a(reinterpret_cast(foo)); Symbolizer symbolizer; ASSERT_TRUE(symbolizer.symbolize(a)); EXPECT_EQ("folly::symbolizer::test::foo()", demangle(a.name.str().c_str()));