|
7 | 7 |
|
8 | 8 | #include "eden/fs/telemetry/ThrowTraceCapture.h" |
9 | 9 |
|
10 | | -#ifdef __linux__ |
11 | | -// ============================================================================= |
12 | | -// Linux: folly::exception_tracer |
13 | | -// |
14 | | -// Uses __cxa_throw hook (via --wrap linker flag) to capture stack frames at |
15 | | -// throw time. Frames are stored in thread-local StackTraceStack and symbolized |
16 | | -// via folly's ELF/DWARF symbolizer. |
17 | | -// ============================================================================= |
18 | | - |
19 | | -#include <sstream> |
| 10 | +#include <cstdlib> |
20 | 11 |
|
21 | | -#include <folly/debugging/exception_tracer/ExceptionTracer.h> |
| 12 | +#include "eden/fs/rust/backtrace_ffi/src/lib.rs.h" |
22 | 13 |
|
23 | | -namespace facebook::eden { |
| 14 | +#if defined(__linux__) |
24 | 15 |
|
25 | | -std::optional<std::string> getThrowSiteStackTrace() { |
26 | | - auto exceptions = folly::exception_tracer::getCurrentExceptions(); |
27 | | - if (!exceptions.empty()) { |
28 | | - std::ostringstream ss; |
29 | | - for (const auto& info : exceptions) { |
30 | | - ss << info; |
31 | | - } |
32 | | - auto trace = ss.str(); |
33 | | - if (!trace.empty()) { |
34 | | - return trace; |
35 | | - } |
36 | | - } |
37 | | - return std::nullopt; |
| 16 | +namespace { |
| 17 | +// Common: onThrow() captures a raw backtrace via Rust FFI. |
| 18 | +// Re-entrancy is handled by the Rust CapturingGuard in capture_backtrace(). |
| 19 | +void onThrow() { |
| 20 | + constexpr size_t kMaxStackDepth = 64; |
| 21 | + facebook::eden::capture_backtrace(kMaxStackDepth); |
38 | 22 | } |
| 23 | +} // namespace |
39 | 24 |
|
40 | | -} // namespace facebook::eden |
| 25 | +// Linux throw hook using the GNU linker's --wrap mechanism. |
| 26 | +// |
| 27 | +// When -Wl,--wrap=__cxa_throw is passed (via exported_linker_flags in BUCK), |
| 28 | +// the linker redirects all calls to __cxa_throw to __wrap___cxa_throw, and |
| 29 | +// makes the original available as __real___cxa_throw. This lets us intercept |
| 30 | +// every C++ throw to capture a backtrace before the stack unwinds. |
| 31 | +// |
| 32 | +// Flow: throw expr → __wrap___cxa_throw → onThrow() → capture_backtrace() |
| 33 | +// → __real___cxa_throw (original, performs the actual throw) |
| 34 | + |
| 35 | +extern "C" { |
| 36 | +void __real___cxa_throw(void*, void*, void (*)(void*)) |
| 37 | + __attribute__((__noreturn__)); |
| 38 | + |
| 39 | +__attribute__((__noreturn__)) void __wrap___cxa_throw( |
| 40 | + void* thrownException, |
| 41 | + void* type, |
| 42 | + void (*destructor)(void*)) { |
| 43 | + onThrow(); |
| 44 | + __real___cxa_throw(thrownException, type, destructor); |
| 45 | + __builtin_unreachable(); |
| 46 | +} |
| 47 | +} // extern "C" |
41 | 48 |
|
42 | | -#else |
43 | | -// ============================================================================= |
44 | | -// Unsupported platform — no throw-site stack traces available. |
45 | | -// ============================================================================= |
| 49 | +#endif // defined(__linux__) |
46 | 50 |
|
47 | 51 | namespace facebook::eden { |
48 | 52 |
|
| 53 | +// Lazy symbolization via Rust FFI — resolves captured frames on demand. |
49 | 54 | std::optional<std::string> getThrowSiteStackTrace() { |
50 | | - return std::nullopt; |
| 55 | + auto trace = symbolize_captured_trace(); |
| 56 | + if (trace.empty()) { |
| 57 | + return std::nullopt; |
| 58 | + } |
| 59 | + return std::string(trace.data(), trace.size()); |
51 | 60 | } |
52 | 61 |
|
53 | 62 | } // namespace facebook::eden |
54 | | - |
55 | | -#endif // __linux__ |
|
0 commit comments