Skip to content

Commit 5e102f7

Browse files
Carlos Torresmeta-codesync[bot]
authored andcommitted
Integrate frame-pointer unwinder into stack trace functions
Summary: Wire up the frame-pointer unwinder in getStackTrace(), getStackTraceSafe(), and getStackTraceHeap() for faster stack traces when FOLLY_FB_UNWINDER_ENABLED=1 is set and kernel 6.11+ with PROCMAP_QUERY is available. The shared logic is factored into a single signal-safe helper, tryFramePointerUnwind(), which invokes the folly_debugging_backtrace_raw weak symbol; all three entry points delegate to it instead of duplicating the unwind block. Falls back automatically to existing unwinders (unw_backtrace, libunwind step loop, or heap-allocated libunwind) when frame-pointer unwinding is disabled, unavailable, or fails. Reviewed By: mcfi, yfeldblum Differential Revision: D90648704 fbshipit-source-id: 5f40718e0b922fdb88a795a40563e1a938dee84b
1 parent 36e44f4 commit 5e102f7

1 file changed

Lines changed: 47 additions & 1 deletion

File tree

‎folly/debugging/symbolizer/StackTrace.cpp‎

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#include <folly/debugging/symbolizer/StackTrace.h>
1818
#include <folly/tracing/AsyncStack.h>
1919

20+
#include <algorithm>
2021
#include <cstdlib>
2122
#include <cstring>
23+
#include <limits>
2224
#include <memory>
2325
#include <optional>
2426

@@ -43,8 +45,12 @@
4345
// dependency and these weak symbols can be removed.
4446
#if FOLLY_HAVE_WEAK_SYMBOLS
4547
extern "C" FOLLY_ATTR_WEAK bool folly_debugging_have_proc_map_query();
48+
extern "C" FOLLY_ATTR_WEAK void folly_debugging_backtrace_raw(
49+
void** vec, unsigned* len, unsigned max_len);
4650
#else
4751
static bool (*folly_debugging_have_proc_map_query)() = nullptr;
52+
static void (*folly_debugging_backtrace_raw)(
53+
void** vec, unsigned* len, unsigned max_len) = nullptr;
4854
#endif
4955

5056
namespace folly {
@@ -85,6 +91,30 @@ static bool isFramePointerUnwinderEnabled() noexcept {
8591
// Force initialization of enable flag during static init phase.
8692
static const bool sFramePointerUnwinderEnabledInit =
8793
isFramePointerUnwinderEnabled();
94+
95+
// Returns frame count (>= 0) when the frame-pointer unwinder is enabled,
96+
// available, and produced at least one frame; returns -1 otherwise (caller
97+
// should fall through to the next unwinder). Mirrors the ssize_t/-1
98+
// convention used by getStackTraceInPlace and the public stack-trace APIs.
99+
FOLLY_ALWAYS_INLINE ssize_t
100+
tryFramePointerUnwind(uintptr_t* addresses, size_t maxAddresses) noexcept {
101+
// Prevent LTO from optimizing away static initialization that pre-runs
102+
// pthread_once before any signal handlers are installed.
103+
std::ignore = sProcMapQueryInit;
104+
105+
if (folly_debugging_backtrace_raw == nullptr ||
106+
!isFramePointerUnwinderEnabled() || !isProcMapQueryAvailable()) {
107+
return -1;
108+
}
109+
unsigned len = 0;
110+
folly_debugging_backtrace_raw(
111+
reinterpret_cast<void**>(addresses),
112+
&len,
113+
static_cast<unsigned>(std::min(
114+
maxAddresses,
115+
static_cast<size_t>(std::numeric_limits<unsigned>::max()))));
116+
return len > 0 ? static_cast<ssize_t>(len) : -1;
117+
}
88118
} // namespace
89119

90120
ssize_t getStackTrace(
@@ -93,6 +123,11 @@ ssize_t getStackTrace(
93123
static_assert(
94124
sizeof(uintptr_t) == sizeof(void*), "uintptr_t / pointer size mismatch");
95125
std::ignore = sInit;
126+
127+
if (ssize_t n = tryFramePointerUnwind(addresses, maxAddresses); n >= 0) {
128+
return n;
129+
}
130+
96131
// The libunwind documentation says that unw_backtrace is
97132
// async-signal-safe but, as of libunwind 1.0.1, it isn't
98133
// (tdep_trace allocates memory on x86_64)
@@ -237,19 +272,30 @@ ssize_t getStackTraceSafe(
237272
// https://opensource.apple.com/source/Libc/Libc-1353.60.8/, and it is
238273
// widely used in signal handlers in practice.
239274
return backtrace(reinterpret_cast<void**>(addresses), maxAddresses);
240-
#elif defined(FOLLY_HAVE_LIBUNWIND) && FOLLY_HAVE_LIBUNWIND
275+
#else
276+
if (ssize_t n = tryFramePointerUnwind(addresses, maxAddresses); n >= 0) {
277+
return n;
278+
}
279+
#if defined(FOLLY_HAVE_LIBUNWIND) && FOLLY_HAVE_LIBUNWIND
241280
unw_context_t context;
242281
unw_cursor_t cursor;
243282
return getStackTraceInPlace(context, cursor, addresses, maxAddresses);
244283
#else
245284
return -1;
246285
#endif
286+
#endif
247287
}
248288

249289
ssize_t getStackTraceHeap(
250290
[[maybe_unused]] uintptr_t* addresses,
251291
[[maybe_unused]] size_t maxAddresses) {
252292
std::ignore = sInit;
293+
294+
// Frame-pointer unwinding needs no large context, so no heap alloc.
295+
if (ssize_t n = tryFramePointerUnwind(addresses, maxAddresses); n >= 0) {
296+
return n;
297+
}
298+
253299
#if defined(FOLLY_HAVE_LIBUNWIND) && FOLLY_HAVE_LIBUNWIND
254300
struct Ctx {
255301
unw_context_t context;

0 commit comments

Comments
 (0)