Skip to content

Commit facc042

Browse files
committed
fixup: inst-at-start=yes tests
1 parent 695a1e9 commit facc042

5 files changed

Lines changed: 387 additions & 106 deletions

File tree

callgrind-utils/tests/snapshot.rs

Lines changed: 2 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -120,35 +120,6 @@ fn run_callgrind_full(bin: &Path) -> String {
120120
.unwrap_or_else(|e| panic!("read {}: {e}", out_file.display()))
121121
}
122122

123-
/// Strip Callgrind's recursion-separation suffix (`fib'2` -> `fib`) so a
124-
/// function and its deeper-recursion clones compare equal.
125-
fn base(name: &str) -> &str {
126-
name.split('\'').next().unwrap_or(name)
127-
}
128-
129-
/// Sum the `call_count` over every edge with these exact caller/callee
130-
/// function names (recursion clones are distinct names, so pass them exactly).
131-
fn sum_counts(g: &CallGraph, caller: &str, callee: &str) -> u64 {
132-
g.edges()
133-
.iter()
134-
.filter(|e| e.caller.function == caller && e.callee.function == callee)
135-
.map(|e| e.call_count.unwrap_or(0))
136-
.sum()
137-
}
138-
139-
/// True if any edge connects these two functions, regardless of count.
140-
fn has_edge(g: &CallGraph, caller: &str, callee: &str) -> bool {
141-
g.edges()
142-
.iter()
143-
.any(|e| e.caller.function == caller && e.callee.function == callee)
144-
}
145-
146-
/// Full-program trace matrix: profile each fixture with `--instr-atstart=yes`
147-
/// and assert version-stable invariants rather than a golden snapshot (the
148-
/// startup frames carry libc/loader-specific names like
149-
/// `__libc_start_main@@GLIBC_2.34` and raw loader addresses, which are not
150-
/// portable). This is both an end-to-end test of full-program tracing and a
151-
/// regression guard for the arm64 `B`-vs-`BL` jump-kind fix.
152123
#[rstest]
153124
#[case("recursion")]
154125
#[case("chain")]
@@ -159,82 +130,7 @@ fn fixture_full_trace(#[case] stem: &str) {
159130
let raw = run_callgrind_full(&bin);
160131
let graph = CallGraph::parse(Cursor::new(raw.as_str()), &ParseOptions::default())
161132
.unwrap_or_else(|e| panic!("parse {stem} full callgrind output: {e:?}"));
133+
let json = graph.to_json().expect("to_json");
162134

163-
// The canonical JSON projection round-trips on a large, real-world graph.
164-
graph.to_json().expect("to_json");
165-
166-
// Full-program capture: `main` is reached as a callee. The scoped run never
167-
// observes this (instrumentation starts inside `main`).
168-
assert!(
169-
graph.edges().iter().any(|e| e.callee.function == "main"),
170-
"{stem}: expected `main` to appear as a callee under --instr-atstart=yes"
171-
);
172-
173-
// The fixture's own source functions all appear in the binary's object
174-
// (which also carries CRT glue like `_start`/`frame_dummy` — hence subset,
175-
// not equality). This is the same self-contained sub-graph as the scoped
176-
// snapshot, now surrounded by — but not merged with — the startup frames.
177-
let own: Vec<&str> = graph
178-
.nodes()
179-
.iter()
180-
.filter(|n| n.object == stem)
181-
.map(|n| n.function.as_str())
182-
.collect();
183-
let expected: &[&str] = match stem {
184-
"recursion" => &["compute", "fib", "fib'2", "main", "square"],
185-
"chain" => &["a", "b", "c", "main"],
186-
"diamond" => &["bottom", "left", "main", "right", "top"],
187-
"mutual" => &["is_even", "is_even'2", "is_odd", "is_odd'2", "main"],
188-
_ => unreachable!("unknown fixture {stem}"),
189-
};
190-
for f in expected {
191-
assert!(
192-
own.contains(f),
193-
"{stem}: expected fixture function `{f}` in object `{stem}`; got {own:?}"
194-
);
195-
}
196-
197-
// Per-fixture call-graph shape (matches the committed scoped snapshots; the
198-
// ZERO_STATS-bounded compute region carries identical counts).
199-
match stem {
200-
"recursion" => {
201-
// Direct recursion: fib(8) makes 2*fib(9)-1 = 67 fib invocations.
202-
// compute->fib=1, fib->fib'2=2, leaving fib'2->fib'2=64. The pre-fix
203-
// arm64 bug inflated this to 98 via phantom base-case "calls".
204-
assert_eq!(sum_counts(&graph, "fib", "fib'2"), 2, "recursion fib->fib'2");
205-
assert_eq!(
206-
sum_counts(&graph, "fib'2", "fib'2"),
207-
64,
208-
"recursion fib'2->fib'2"
209-
);
210-
}
211-
"chain" => {
212-
assert_eq!(sum_counts(&graph, "main", "a"), 1, "chain main->a");
213-
assert_eq!(sum_counts(&graph, "a", "b"), 1, "chain a->b");
214-
assert_eq!(sum_counts(&graph, "b", "c"), 1, "chain b->c");
215-
}
216-
"diamond" => {
217-
assert!(has_edge(&graph, "top", "left"), "diamond top->left");
218-
assert!(has_edge(&graph, "top", "right"), "diamond top->right");
219-
assert!(has_edge(&graph, "left", "bottom"), "diamond left->bottom");
220-
assert!(has_edge(&graph, "right", "bottom"), "diamond right->bottom");
221-
}
222-
"mutual" => {
223-
// is_even/is_odd are *mutually* recursive: neither ever calls itself.
224-
// The arm64 B-vs-BL bug invented self-edges (is_even->is_even'2 etc.);
225-
// guard against their return.
226-
for e in graph.edges() {
227-
let (cb, kb) = (base(&e.caller.function), base(&e.callee.function));
228-
if cb == "is_even" || cb == "is_odd" {
229-
assert_ne!(cb, kb, "mutual: spurious self-edge {e:?}");
230-
}
231-
}
232-
assert!(has_edge(&graph, "is_even", "is_odd"), "mutual is_even->is_odd");
233-
assert!(
234-
has_edge(&graph, "is_odd", "is_even'2"),
235-
"mutual is_odd->is_even'2"
236-
);
237-
}
238-
_ => unreachable!("unknown fixture {stem}"),
239-
}
135+
insta::assert_snapshot!(format!("{stem}_full__json"), json);
240136
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
source: tests/snapshot.rs
3+
expression: json
4+
---
5+
{
6+
"nodes": [
7+
{
8+
"function": "(below main)",
9+
"file": "???",
10+
"object": "chain"
11+
},
12+
{
13+
"function": "a",
14+
"file": "chain.c",
15+
"object": "chain"
16+
},
17+
{
18+
"function": "b",
19+
"file": "chain.c",
20+
"object": "chain"
21+
},
22+
{
23+
"function": "c",
24+
"file": "chain.c",
25+
"object": "chain"
26+
},
27+
{
28+
"function": "main",
29+
"file": "chain.c",
30+
"object": "chain"
31+
},
32+
{
33+
"function": "0x000000000001fd40",
34+
"file": "???",
35+
"object": "ld-linux-x86-64.so.2"
36+
},
37+
{
38+
"function": "(below main)",
39+
"file": "???",
40+
"object": "libc.so.6"
41+
},
42+
{
43+
"function": "__libc_start_main@@GLIBC_2.34",
44+
"file": "???",
45+
"object": "libc.so.6"
46+
}
47+
],
48+
"edges": [
49+
{
50+
"caller": 0,
51+
"callee": 7,
52+
"call_count": 0
53+
},
54+
{
55+
"caller": 1,
56+
"callee": 2,
57+
"call_count": 1
58+
},
59+
{
60+
"caller": 2,
61+
"callee": 3,
62+
"call_count": 1
63+
},
64+
{
65+
"caller": 4,
66+
"callee": 1,
67+
"call_count": 1
68+
},
69+
{
70+
"caller": 5,
71+
"callee": 0,
72+
"call_count": 0
73+
},
74+
{
75+
"caller": 6,
76+
"callee": 4,
77+
"call_count": 0
78+
},
79+
{
80+
"caller": 7,
81+
"callee": 6,
82+
"call_count": 0
83+
}
84+
]
85+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
source: tests/snapshot.rs
3+
expression: json
4+
---
5+
{
6+
"nodes": [
7+
{
8+
"function": "(below main)",
9+
"file": "???",
10+
"object": "diamond"
11+
},
12+
{
13+
"function": "bottom",
14+
"file": "diamond.c",
15+
"object": "diamond"
16+
},
17+
{
18+
"function": "left",
19+
"file": "diamond.c",
20+
"object": "diamond"
21+
},
22+
{
23+
"function": "main",
24+
"file": "diamond.c",
25+
"object": "diamond"
26+
},
27+
{
28+
"function": "right",
29+
"file": "diamond.c",
30+
"object": "diamond"
31+
},
32+
{
33+
"function": "top",
34+
"file": "diamond.c",
35+
"object": "diamond"
36+
},
37+
{
38+
"function": "0x000000000001fd40",
39+
"file": "???",
40+
"object": "ld-linux-x86-64.so.2"
41+
},
42+
{
43+
"function": "(below main)",
44+
"file": "???",
45+
"object": "libc.so.6"
46+
},
47+
{
48+
"function": "__libc_start_main@@GLIBC_2.34",
49+
"file": "???",
50+
"object": "libc.so.6"
51+
}
52+
],
53+
"edges": [
54+
{
55+
"caller": 0,
56+
"callee": 8,
57+
"call_count": 0
58+
},
59+
{
60+
"caller": 2,
61+
"callee": 1,
62+
"call_count": 1
63+
},
64+
{
65+
"caller": 3,
66+
"callee": 5,
67+
"call_count": 1
68+
},
69+
{
70+
"caller": 4,
71+
"callee": 1,
72+
"call_count": 1
73+
},
74+
{
75+
"caller": 5,
76+
"callee": 2,
77+
"call_count": 1
78+
},
79+
{
80+
"caller": 5,
81+
"callee": 4,
82+
"call_count": 1
83+
},
84+
{
85+
"caller": 6,
86+
"callee": 0,
87+
"call_count": 0
88+
},
89+
{
90+
"caller": 7,
91+
"callee": 3,
92+
"call_count": 0
93+
},
94+
{
95+
"caller": 8,
96+
"callee": 7,
97+
"call_count": 0
98+
}
99+
]
100+
}

0 commit comments

Comments
 (0)