<< (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
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 (...) {
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);
}
}
} // namespace
-ssize_t getStackTrace(AddressInfo* addresses,
+ssize_t getStackTrace(FrameInfo* addresses,
size_t maxAddresses,
size_t skip) {
unw_context_t uctx;
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);
--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];
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?");
}
}
-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) {
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
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) { }
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) { }
/**
* 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;
}
*/
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:
};
/**
- * 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
}
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()));