Skip to content

Commit eb9b905

Browse files
Chandhana Solainathanmeta-codesync[bot]
authored andcommitted
telemetry: Replace folly exception_tracer with backtrace-rs on Linux
Summary: Replaces folly::exception_tracer with backtrace_ffi for throw-site stack trace capture on Linux. Hooks __cxa_throw via the --wrap linker flag, captures raw frames at throw time through onThrow() with a re-entrancy guard to prevent recursion, and symbolizes lazily when getThrowSiteStackTrace() is called. The folly dependency only supported Linux; this change is the first step toward cross-platform support. On non-Linux platforms, getThrowSiteStackTrace() gracefully returns nullopt until platform hooks are added in subsequent commits. library used: https://github.com/rust-lang/backtrace-rs internally in `eden/fs/rust/backtrace_ffi/src/lib.rs.h` Reviewed By: vilatto Differential Revision: D102215367 fbshipit-source-id: 64d69f563fef3eccf3e7deb9fef38a2d000f9c3a
1 parent 25c9098 commit eb9b905

4 files changed

Lines changed: 54 additions & 52 deletions

File tree

eden/fs/telemetry/BUCK

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,15 @@ cpp_library(
148148
headers = [
149149
"ThrowTraceCapture.h",
150150
],
151+
exported_linker_flags = select({
152+
"DEFAULT": [],
153+
"ovr_config//os:linux": [
154+
"-Wl,--wrap=__cxa_throw",
155+
],
156+
}),
151157
deps = [
152-
"//folly/debugging/exception_tracer:exception_tracer_base",
153-
],
154-
exported_deps = [
155-
"//folly/debugging/exception_tracer:exception_tracer", # @manual
158+
"//eden/fs/rust/backtrace_ffi:backtrace-ffi",
159+
"//eden/fs/rust/backtrace_ffi:backtrace-ffi@header",
156160
],
157161
)
158162

eden/fs/telemetry/ThrowTraceCapture.cpp

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,56 @@
77

88
#include "eden/fs/telemetry/ThrowTraceCapture.h"
99

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>
2011

21-
#include <folly/debugging/exception_tracer/ExceptionTracer.h>
12+
#include "eden/fs/rust/backtrace_ffi/src/lib.rs.h"
2213

23-
namespace facebook::eden {
14+
#if defined(__linux__)
2415

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);
3822
}
23+
} // namespace
3924

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"
4148

42-
#else
43-
// =============================================================================
44-
// Unsupported platform — no throw-site stack traces available.
45-
// =============================================================================
49+
#endif // defined(__linux__)
4650

4751
namespace facebook::eden {
4852

53+
// Lazy symbolization via Rust FFI — resolves captured frames on demand.
4954
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());
5160
}
5261

5362
} // namespace facebook::eden
54-
55-
#endif // __linux__

eden/fs/telemetry/ThrowTraceCapture.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ namespace facebook::eden {
1616
* Returns the throw-site stack trace for the current exception.
1717
* Must be called inside a catch block.
1818
*
19-
* Platform-specific implementations:
20-
* Linux: folly::exception_tracer (hooks __cxa_throw via --wrap)
21-
* macOS: Not yet implemented (returns std::nullopt)
22-
* Windows: Not yet implemented (returns std::nullopt)
19+
* Uses backtrace-rs via Rust FFI for cross-platform stack trace capture.
20+
* Raw IP addresses are captured at throw time; symbolization is deferred
21+
* until this function is called (lazy symbolization).
22+
*
23+
* Platform-specific hooks:
24+
* Linux: __wrap___cxa_throw (via --wrap linker flag)
2325
*/
2426
std::optional<std::string> getThrowSiteStackTrace();
2527

eden/fs/telemetry/test/EdenErrorInfoBuilderTest.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,6 @@ TEST(EdenErrorInfoTest, InitializeFuseEdenErrorInfoWithException) {
2727
try {
2828
throwRuntimeError();
2929
} catch (const std::exception& ex) {
30-
// ErrorArg captures the throw-site stack trace inside a catch block.
31-
ErrorArg error(ex);
32-
#ifdef __linux__
33-
ASSERT_TRUE(error.stackTrace.has_value())
34-
<< "ErrorArg should capture a stack trace from a thrown exception";
35-
EXPECT_NE(error.stackTrace->find("throwRuntimeError"), std::string::npos)
36-
<< "Stack trace should contain the throwing function, got: "
37-
<< *error.stackTrace;
38-
#endif
39-
40-
// create() stores the raw combined trace in info.stackTrace.
4130
auto info = EdenErrorInfo::fuse(ex, 42, "/mnt/repo").create();
4231

4332
EXPECT_EQ(info.component, EdenComponent::Fuse);

0 commit comments

Comments
 (0)