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) {
<< (info.frames.size() == 1 ? " frame" : " frames")
<< ")\n";
try {
std::vector<AddressInfo> addresses;
std::vector<FrameInfo> 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 (...) {
......
......@@ -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<kMaxStackTraceDepth> 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);
}
}
......
......@@ -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
......@@ -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 <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
* 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 <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 {
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 <size_t N>
void symbolize(FrameArray<N>& 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 <size_t N>
void print(const FrameArray<N>& 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
......
......@@ -27,7 +27,7 @@ void foo() {
}
TEST(Symbolizer, Single) {
AddressInfo a(reinterpret_cast<uintptr_t>(foo));
FrameInfo a(reinterpret_cast<uintptr_t>(foo));
Symbolizer symbolizer;
ASSERT_TRUE(symbolizer.symbolize(a));
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