Skip to content

Commit 07f0b66

Browse files
author
James Gober
committed
Fixed 0.9.4
1 parent 73aca0e commit 07f0b66

3 files changed

Lines changed: 49 additions & 11 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ with `cargo run --release --example bench_overhead`:
163163
Tier 1 comes in well under the 50 ns target from the spec
164164
([`REPS.md`](REPS.md) section 6). Tier 2 is currently above the
165165
200 ns target in that section; closing that gap is tracked for
166-
v0.9.1.1. The Tier 2 path is correct and recursion-safe in the
166+
v0.9.6. The Tier 2 path is correct and recursion-safe in the
167167
current release; the optimisation is a separate, focused pass.
168168

169169
## Why a new allocation profiler
@@ -186,11 +186,11 @@ demand.
186186
| Name-claim placeholder | `v0.1.0` | shipped |
187187
| Real `GlobalAlloc` + Tier 1 counters | `v0.9.0` | shipped |
188188
| Tier 2: inline backtrace capture | `v0.9.1` | shipped |
189-
| Tier 2 perf optimisation | `v0.9.1.1` | planned |
190189
| Symbolication for reports | `v0.9.2` | shipped |
191190
| Tier 3: DHAT-compatible JSON output | `v0.9.3` | shipped |
192191
| dhat-rs drop-in compat surface | `v0.9.4` | shipped |
193192
| `dev-bench` swap (consumer side) | `v0.9.5` | planned |
193+
| Tier 2 perf optimisation | `v0.9.6` | planned |
194194
| Stable API (`1.0`) | `v1.0.0` | planned |
195195

196196
The `1.0` release freezes the public API and the wire format.

dhat-heap.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"dhatFileVersion":2,"mode":"rust-heap","verb":"Allocated","bklt":false,"bkacc":false,"bu":"byte","bsu":"bytes","bksu":"blocks","tu":"instrs","Mtu":"Minstr","tuth":0,"cmd":"C:\\Dev\\libraries\\Rust\\mod-alloc\\target\\debug\\deps\\mod_alloc-706d9fc9797ded60.exe","pid":35544,"tg":0,"te":0,"pps":[{"tb":64,"tbk":1,"eb":0,"ebk":0,"fs":[1,2,3]}],"ftbl":["[root]","0x1000: std::collections::hash::set::HashSet<T>::new::hc8018216f96c1513","0x2000: <core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::next::h1f41dd7f8faabf33","0x3000: core::slice::sort::stable::drift::sort::h10052e29f271759c"]}

tests/dhat_compat_surface.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,28 @@
33
44
#![cfg(feature = "dhat-compat")]
55

6+
use std::sync::Mutex;
7+
68
use mod_alloc::dhat_compat::{ad_hoc_event, AdHocStats, Alloc, HeapStats, Profiler};
79

810
#[global_allocator]
911
static ALLOC: Alloc = Alloc;
1012

13+
// `live_count` / `curr_blocks` is a process-wide counter shared
14+
// across every test in this binary. Cargo runs tests in parallel
15+
// by default, so a test that allocates and a test that reads the
16+
// counter race continuously. We serialise *every* test in this
17+
// file behind one mutex so the precise-relative claims about
18+
// `curr_blocks` in `live_block_count_rises_and_falls` are not
19+
// disturbed by allocations from sibling tests running in parallel
20+
// threads. The file's total runtime stays under a second so
21+
// sequential execution costs nothing.
22+
static TEST_LOCK: Mutex<()> = Mutex::new(());
23+
24+
fn lock() -> std::sync::MutexGuard<'static, ()> {
25+
TEST_LOCK.lock().unwrap_or_else(|p| p.into_inner())
26+
}
27+
1128
#[inline(never)]
1229
fn workload(n: usize) -> Vec<Vec<u8>> {
1330
let mut keep: Vec<Vec<u8>> = Vec::with_capacity(n);
@@ -19,6 +36,7 @@ fn workload(n: usize) -> Vec<Vec<u8>> {
1936

2037
#[test]
2138
fn alloc_swap_pattern_compiles_and_tracks_total_bytes() {
39+
let _g = lock();
2240
let before = HeapStats::get();
2341
let kept = workload(16);
2442
let after = HeapStats::get();
@@ -35,28 +53,43 @@ fn alloc_swap_pattern_compiles_and_tracks_total_bytes() {
3553

3654
#[test]
3755
fn live_block_count_rises_and_falls() {
38-
let baseline = HeapStats::get().curr_blocks;
56+
let _g = lock();
57+
58+
// `curr_blocks` is a process-wide instantaneous counter. The
59+
// test harness runs worker threads that allocate and
60+
// deallocate outside our mutex, and `max_blocks` (the
61+
// high-water mark) may already exceed any modest delta we
62+
// can produce — so neither `before+N` nor `max + N` is a
63+
// race-free assertion.
64+
//
65+
// What IS provable: while we *own* `kept_count` live
66+
// allocations, the process-wide `curr_blocks` count
67+
// must include them — it cannot drop below `kept_count`
68+
// because we have not released them yet.
3969
let kept = workload(8);
40-
let peak = HeapStats::get();
4170
let kept_count = kept.len();
71+
let with_kept = HeapStats::get();
4272
drop(kept);
43-
let after = HeapStats::get();
73+
let after_drop = HeapStats::get();
4474

4575
assert!(
46-
peak.curr_blocks >= baseline + kept_count,
47-
"curr_blocks {} should be at least baseline {} + {}",
48-
peak.curr_blocks,
49-
baseline,
76+
with_kept.curr_blocks >= kept_count,
77+
"while {} vecs are alive, curr_blocks ({}) must be >= {}",
78+
kept_count,
79+
with_kept.curr_blocks,
5080
kept_count
5181
);
5282
assert!(
53-
after.curr_blocks <= peak.curr_blocks,
54-
"curr_blocks should fall after drop"
83+
after_drop.max_blocks >= with_kept.max_blocks,
84+
"max_blocks is monotonic; after_drop ({}) must be >= with_kept ({})",
85+
after_drop.max_blocks,
86+
with_kept.max_blocks
5587
);
5688
}
5789

5890
#[test]
5991
fn profiler_drop_writes_file_with_dhat_json_shape() {
92+
let _g = lock();
6093
let path = std::env::temp_dir().join(format!(
6194
"mod-alloc-dhat-compat-test-{}-{}.json",
6295
std::process::id(),
@@ -93,6 +126,7 @@ fn profiler_drop_writes_file_with_dhat_json_shape() {
93126

94127
#[test]
95128
fn testing_mode_suppresses_drop_write() {
129+
let _g = lock();
96130
let path = std::env::temp_dir().join(format!(
97131
"mod-alloc-dhat-compat-testing-{}-{}.json",
98132
std::process::id(),
@@ -119,6 +153,7 @@ fn testing_mode_suppresses_drop_write() {
119153

120154
#[test]
121155
fn ad_hoc_event_accumulates_counts_and_weights() {
156+
let _g = lock();
122157
let before = AdHocStats::get();
123158
ad_hoc_event(7);
124159
ad_hoc_event(3);
@@ -129,6 +164,7 @@ fn ad_hoc_event_accumulates_counts_and_weights() {
129164

130165
#[test]
131166
fn trim_backtraces_accepts_oversize_value_without_panic() {
167+
let _g = lock();
132168
// 100 > walker cap of 8 — must not panic, must still build.
133169
let _p = Profiler::builder()
134170
.testing()
@@ -138,6 +174,7 @@ fn trim_backtraces_accepts_oversize_value_without_panic() {
138174

139175
#[test]
140176
fn profiler_new_heap_constructs_and_drops_cleanly() {
177+
let _g = lock();
141178
// Default `dhat-heap.json` write to CWD would litter the
142179
// workspace, so we route it to tmp instead.
143180
let path = std::env::temp_dir().join(format!(

0 commit comments

Comments
 (0)