Commit 730a0da8 authored by Tudor Bosman's avatar Tudor Bosman Committed by Jordan DeLong

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
parent db56587b
...@@ -54,7 +54,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) { ...@@ -54,7 +54,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
<< (info.frames.size() == 1 ? " frame" : " frames") << (info.frames.size() == 1 ? " frame" : " frames")
<< ")\n"; << ")\n";
try { try {
std::vector<AddressInfo> addresses; std::vector<FrameInfo> addresses;
addresses.reserve(info.frames.size()); addresses.reserve(info.frames.size());
for (auto ip : info.frames) { for (auto ip : info.frames) {
// Symbolize the previous address because the IP might be in the // Symbolize the previous address because the IP might be in the
...@@ -66,7 +66,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) { ...@@ -66,7 +66,7 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
symbolizer.symbolize(addresses.data(), addresses.size()); symbolizer.symbolize(addresses.data(), addresses.size());
OStreamSymbolizePrinter osp(out); OStreamSymbolizePrinter osp(out);
osp.print(addresses.data(), addresses.size()); osp.print(addresses.data(), addresses.size(), addresses.size());
} catch (const std::exception& e) { } catch (const std::exception& e) {
out << "\n !! caught " << folly::exceptionStr(e) << "\n"; out << "\n !! caught " << folly::exceptionStr(e) << "\n";
} catch (...) { } catch (...) {
......
...@@ -193,18 +193,17 @@ void dumpStackTrace() { ...@@ -193,18 +193,17 @@ void dumpStackTrace() {
SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); }; SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
// Get and symbolize stack trace // Get and symbolize stack trace
constexpr size_t kMaxStackTraceDepth = 100; constexpr size_t kMaxStackTraceDepth = 100;
AddressInfo addresses[kMaxStackTraceDepth]; FrameArray<kMaxStackTraceDepth> addresses;
// Skip the getStackTrace frame // Skip the getStackTrace frame
ssize_t stackTraceDepth = getStackTrace(addresses, kMaxStackTraceDepth, 1); if (!getStackTrace(addresses)) {
if (stackTraceDepth < 0) {
print("(error retrieving stack trace)\n"); print("(error retrieving stack trace)\n");
} else { } else {
Symbolizer symbolizer; Symbolizer symbolizer;
symbolizer.symbolize(addresses, stackTraceDepth); symbolizer.symbolize(addresses);
FDSymbolizePrinter printer(STDERR_FILENO); FDSymbolizePrinter printer(STDERR_FILENO);
printer.print(addresses, stackTraceDepth); printer.print(addresses);
} }
} }
......
...@@ -148,7 +148,7 @@ bool parseProcMapsLine(StringPiece line, ...@@ -148,7 +148,7 @@ bool parseProcMapsLine(StringPiece line,
} // namespace } // namespace
ssize_t getStackTrace(AddressInfo* addresses, ssize_t getStackTrace(FrameInfo* addresses,
size_t maxAddresses, size_t maxAddresses,
size_t skip) { size_t skip) {
unw_context_t uctx; unw_context_t uctx;
...@@ -160,7 +160,7 @@ ssize_t getStackTrace(AddressInfo* addresses, ...@@ -160,7 +160,7 @@ ssize_t getStackTrace(AddressInfo* addresses,
unw_cursor_t cursor; unw_cursor_t cursor;
size_t idx = 0; size_t idx = 0;
bool first = true; bool first = true;
while (idx < maxAddresses) { for (;;) {
if (first) { if (first) {
first = false; first = false;
r = unw_init_local(&cursor, &uctx); r = unw_init_local(&cursor, &uctx);
...@@ -178,6 +178,8 @@ ssize_t getStackTrace(AddressInfo* addresses, ...@@ -178,6 +178,8 @@ ssize_t getStackTrace(AddressInfo* addresses,
--skip; --skip;
continue; continue;
} }
if (idx < maxAddresses) {
unw_word_t ip; unw_word_t ip;
int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip); int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip);
if (rr < 0) { if (rr < 0) {
...@@ -186,18 +188,15 @@ ssize_t getStackTrace(AddressInfo* addresses, ...@@ -186,18 +188,15 @@ ssize_t getStackTrace(AddressInfo* addresses,
// If error, assume not a signal frame // If error, assume not a signal frame
rr = unw_is_signal_frame(&cursor); rr = unw_is_signal_frame(&cursor);
addresses[idx] = FrameInfo(ip, (rr > 0));
addresses[idx++] = AddressInfo(ip, (rr > 0));
} }
++idx;
if (r < 0) {
return -1;
} }
return idx; return idx;
} }
void Symbolizer::symbolize(AddressInfo* addresses, size_t addressCount) { void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) {
size_t remaining = 0; size_t remaining = 0;
for (size_t i = 0; i < addressCount; ++i) { for (size_t i = 0; i < addressCount; ++i) {
auto& ainfo = addresses[i]; auto& ainfo = addresses[i];
...@@ -305,7 +304,7 @@ namespace { ...@@ -305,7 +304,7 @@ namespace {
const char kHexChars[] = "0123456789abcdef"; const char kHexChars[] = "0123456789abcdef";
} // namespace } // namespace
void SymbolizePrinter::print(const AddressInfo& ainfo) { void SymbolizePrinter::print(const FrameInfo& ainfo) {
uintptr_t address = ainfo.address; uintptr_t address = ainfo.address;
// Can't use sprintf, not async-signal-safe // Can't use sprintf, not async-signal-safe
static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?"); static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
...@@ -372,12 +371,22 @@ void SymbolizePrinter::print(const AddressInfo& ainfo) { ...@@ -372,12 +371,22 @@ void SymbolizePrinter::print(const AddressInfo& ainfo) {
} }
} }
void SymbolizePrinter::print(const AddressInfo* addresses, void SymbolizePrinter::print(const FrameInfo* addresses,
size_t addressCount) { size_t addressesSize,
for (size_t i = 0; i < addressCount; ++i) { size_t frameCount) {
for (size_t i = 0; i < std::min(addressesSize, frameCount); ++i) {
auto& ainfo = addresses[i]; auto& ainfo = addresses[i];
print(ainfo); 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) { void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
...@@ -388,11 +397,29 @@ void FDSymbolizePrinter::doPrint(StringPiece sp) { ...@@ -388,11 +397,29 @@ void FDSymbolizePrinter::doPrint(StringPiece sp) {
writeFull(fd_, sp.data(), sp.size()); 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); OStreamSymbolizePrinter osp(out);
osp.print(ainfo); osp.print(ainfo);
return out; 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 symbolizer
} // namespace folly } // namespace folly
...@@ -30,13 +30,13 @@ namespace folly { ...@@ -30,13 +30,13 @@ namespace folly {
namespace symbolizer { 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, * 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 { struct FrameInfo {
/* implicit */ AddressInfo(uintptr_t a=0, bool sf=false) /* implicit */ FrameInfo(uintptr_t a=0, bool sf=false)
: address(a), : address(a),
isSignalFrame(sf), isSignalFrame(sf),
found(false) { } found(false) { }
...@@ -47,15 +47,46 @@ struct AddressInfo { ...@@ -47,15 +47,46 @@ struct AddressInfo {
Dwarf::LocationInfo location; Dwarf::LocationInfo location;
}; };
template <size_t N>
struct FrameArray {
FrameArray() : frameCount(0) { }
size_t frameCount;
FrameInfo frames[N];
};
/** /**
* Get the current stack trace into addresses, which has room for at least * Get the current stack trace into addresses, which has room for at least
* maxAddresses entries. Skip the first (topmost) skip entries. * maxAddresses frames. Skip the first (topmost) skip entries.
* Returns the number of entries in addresses on success, -1 on failure. *
* 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 maxAddresses,
size_t skip=0); 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 <size_t N>
bool getStackTrace(FrameArray<N>& 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 { class Symbolizer {
public: public:
Symbolizer() : fileCount_(0) { } Symbolizer() : fileCount_(0) { }
...@@ -63,12 +94,17 @@ class Symbolizer { ...@@ -63,12 +94,17 @@ class Symbolizer {
/** /**
* Symbolize given addresses. * Symbolize given addresses.
*/ */
void symbolize(AddressInfo* addresses, size_t addressCount); void symbolize(FrameInfo* addresses, size_t addressCount);
template <size_t N>
void symbolize(FrameArray<N>& fa) {
symbolize(fa.frames, std::min(fa.frameCount, N));
}
/** /**
* Shortcut to symbolize one address. * Shortcut to symbolize one address.
*/ */
bool symbolize(AddressInfo& address) { bool symbolize(FrameInfo& address) {
symbolize(&address, 1); symbolize(&address, 1);
return address.found; return address.found;
} }
...@@ -86,8 +122,15 @@ class Symbolizer { ...@@ -86,8 +122,15 @@ class Symbolizer {
*/ */
class SymbolizePrinter { class SymbolizePrinter {
public: public:
void print(const AddressInfo& ainfo); void print(const FrameInfo& ainfo);
void print(const AddressInfo* addresses, size_t addressCount); void print(const FrameInfo* addresses,
size_t addressesSize,
size_t frameCount);
template <size_t N>
void print(const FrameArray<N>& fa) {
print(fa.frames, N, fa.frameCount);
}
virtual ~SymbolizePrinter() { } virtual ~SymbolizePrinter() { }
private: private:
...@@ -119,12 +162,12 @@ class FDSymbolizePrinter : public SymbolizePrinter { ...@@ -119,12 +162,12 @@ class FDSymbolizePrinter : public SymbolizePrinter {
}; };
/** /**
* Print an AddressInfo to a stream. Note that the Symbolizer that * Print an FrameInfo to a stream. Note that the Symbolizer that
* symbolized the address must outlive the AddressInfo. Just like * symbolized the address must outlive the FrameInfo. Just like
* OStreamSymbolizePrinter (which it uses internally), this is not * OStreamSymbolizePrinter (which it uses internally), this is not
* reentrant; do not use from signal handling code. * 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 symbolizer
} // namespace folly } // namespace folly
......
...@@ -27,7 +27,7 @@ void foo() { ...@@ -27,7 +27,7 @@ void foo() {
} }
TEST(Symbolizer, Single) { TEST(Symbolizer, Single) {
AddressInfo a(reinterpret_cast<uintptr_t>(foo)); FrameInfo a(reinterpret_cast<uintptr_t>(foo));
Symbolizer symbolizer; Symbolizer symbolizer;
ASSERT_TRUE(symbolizer.symbolize(a)); ASSERT_TRUE(symbolizer.symbolize(a));
EXPECT_EQ("folly::symbolizer::test::foo()", demangle(a.name.str().c_str())); EXPECT_EQ("folly::symbolizer::test::foo()", demangle(a.name.str().c_str()));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment