@@ -7,6 +7,103 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
88## [ Unreleased]
99
10+ ### Added
11+
12+ - ** Tier 2: inline backtrace capture (` backtraces ` feature).** Each
13+ tracked allocation, zero-init allocation, and reallocation
14+ captures up to 8 frames of its call site via inline
15+ frame-pointer walking. Available on ` x86_64 ` and ` aarch64 ` .
16+ Other architectures compile but capture is a no-op.
17+ - ** ` ModAlloc::call_sites() ` ** drains the per-call-site
18+ aggregation table into a ` Vec<CallSiteStats> ` . Each row carries
19+ raw return addresses (top of stack first), the number of
20+ allocations attributed to the site, and the total bytes.
21+ Symbolication ships in v0.9.2.
22+ - ** ` CallSiteStats ` ** public type behind the ` backtraces ` feature.
23+ - ** Per-thread arena** (64 KB OS-page region per thread, 512
24+ events per flush) and ** global aggregation table** (4,096
25+ buckets by default, ~ 384 KB) allocated through raw
26+ ` mmap ` / ` VirtualAlloc ` so the backtrace path never recurses
27+ into ` ModAlloc::alloc ` for its own state.
28+ - ** ` MOD_ALLOC_BUCKETS ` env var** to override the
29+ aggregation-table size at process start. Value is rounded up to
30+ the next power of two and clamped to ` [64, 1_048_576] ` .
31+ - ** ` build.rs ` ** (one-off approved exception): warns at compile
32+ time when ` RUSTFLAGS ` is missing ` -C force-frame-pointers=yes `
33+ and the ` backtraces ` feature is on. See
34+ ` .dev/DIRECTIVES.md ` section 2.1 for the documented exception.
35+ - ** ` .cargo/config.toml ` ** in the crate root enables frame
36+ pointers for the crate's own builds so the test suite and
37+ examples produce useful traces. Downstream consumers must
38+ enable the flag in their own builds.
39+ - New tests:
40+ - ` tests/backtrace_real_chain.rs ` : captures from a deeply
41+ nested ` #[inline(never)] ` call chain.
42+ - ` tests/backtrace_fuzz.rs ` : SplitMix64-driven random workload
43+ proving the walker is total under varied allocation patterns
44+ (10,000 iterations).
45+ - ` tests/backtrace_concurrent.rs ` : 32-thread aggregation
46+ stress test.
47+ - ` src/backtrace/* ` unit tests cover hash determinism, walker
48+ safety checks (null, alignment, out-of-range, non-monotonic,
49+ max-frame cap), arena round-trip, table claim races, and
50+ stack-bounds discovery.
51+ - ** ` examples/backtraces.rs ` ** demonstrates installing
52+ ` ModAlloc ` , exercising a few distinct call paths, and printing
53+ the top sites by total bytes.
54+ - ** CI: AddressSanitizer nightly job.** A dedicated job in
55+ ` .github/workflows/ci.yml ` runs the test suite under
56+ ` -Zsanitizer=address ` on Linux x86_64 to catch any UB in the
57+ unsafe FP-walker path that survives the in-walker safety
58+ checks.
59+
60+ ### Changed
61+
62+ - ` GlobalAlloc::alloc ` , ` alloc_zeroed ` , and ` realloc ` invoke
63+ ` backtrace::record_event ` after the existing counter update
64+ when the ` backtraces ` feature is on. ` dealloc ` does not capture
65+ (matches dhat: call sites describe who allocated, not who
66+ freed).
67+ - Per maintainer guidance, realloc captures all events including
68+ shrinks, matching dhat's per-event accounting. Documented in
69+ the rustdoc.
70+ - CI workflow runs the ` backtraces ` test suite with
71+ ` RUSTFLAGS="-C force-frame-pointers=yes" ` so traces are
72+ meaningful on hosted runners.
73+
74+ ### Design notes
75+
76+ - ** Reentrancy on the backtrace path.** The walker reads memory
77+ inside the cached stack bounds (which are queried via
78+ ` GetCurrentThreadStackLimits ` on Windows,
79+ ` pthread_getattr_np ` on Linux, ` pthread_get_stackaddr_np ` on
80+ Darwin / BSD). All reads are pointer-aligned and in-range; no
81+ page faults are possible. The existing ` IN_ALLOC ` reentrancy
82+ guard from v0.9.0 catches any pathological allocation
83+ triggered transitively from inside the backtrace path (e.g.
84+ libc lazy-init during the first ` pthread_getattr_np ` ).
85+ - ** No ` HashMap ` in the hot path.** The global aggregation table
86+ is a fixed-size open-addressed array allocated once via raw OS
87+ pages, with atomic per-bucket CAS for claim and linear probing
88+ for index collisions. Hash collisions on the 64-bit FxHash are
89+ a documented limitation (different sites with identical hashes
90+ get conflated).
91+ - ** Bucket publish protocol.** Each bucket uses a two-phase
92+ claim: CAS on ` hash ` first (Release), then write
93+ ` sample_frames ` , then store ` frame_count ` with Release. Readers
94+ gate on ` frame_count > 0 ` after observing a non-zero hash;
95+ this prevents torn reads of the sample frames.
96+
97+ ### Migration
98+
99+ The default build (Tier 1 only) is unchanged. Existing callers
100+ need no edits.
101+
102+ Users opting in to the ` backtraces ` feature must add
103+ ` -C force-frame-pointers=yes ` to their build configuration. The
104+ included ` build.rs ` emits a ` cargo:warning= ` at compile time if
105+ this is missing.
106+
10107## [ 0.9.0] - 2026-05-13
11108
12109### Added
0 commit comments