Skip to content

Commit 7d55d1c

Browse files
art049claude
andcommitted
test(tracegrind): add control flow regression tests
Add tests for signal handling, C++ exceptions, longjmp, tail calls, and deep recursion (100 levels) to verify call stack correctness across non-trivial control flow. Also fix missing -I include path for tracegrind.h in test Makefile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f2334e7 commit 7d55d1c

21 files changed

Lines changed: 316 additions & 1 deletion

tracegrind/tests/Makefile.am

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ check_PROGRAMS = \
1414
test_foo_bar_baz.bin \
1515
test_inline.bin \
1616
test_enter_inlined.bin \
17-
test_nested_inlined.bin
17+
test_nested_inlined.bin \
18+
test_signal.bin \
19+
test_exception.bin \
20+
test_longjmp.bin \
21+
test_tailcall.bin \
22+
test_recursion.bin
1823

24+
AM_CPPFLAGS += -I$(top_srcdir)/tracegrind
1925
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
2026
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
2127

@@ -30,6 +36,12 @@ test_enter_inlined_bin_SOURCES = test_enter_inlined.c
3036
test_enter_inlined_bin_CFLAGS = $(AM_CFLAGS) -O2 -g
3137
test_nested_inlined_bin_SOURCES = test_nested_inlined.c
3238
test_nested_inlined_bin_CFLAGS = $(AM_CFLAGS) -O1 -g
39+
test_signal_bin_SOURCES = test_signal.c
40+
test_exception_bin_SOURCES = test_exception.cpp
41+
test_longjmp_bin_SOURCES = test_longjmp.c
42+
test_tailcall_bin_SOURCES = test_tailcall.c
43+
test_tailcall_bin_CFLAGS = $(AM_CFLAGS) -O2 -g
44+
test_recursion_bin_SOURCES = test_recursion.c
3345

3446
EXTRA_DIST = \
3547
test_basic.vgtest test_basic.stderr.exp test_basic.post.exp \
@@ -40,4 +52,9 @@ EXTRA_DIST = \
4052
test_inline.vgtest test_inline.stderr.exp test_inline.post.exp \
4153
test_enter_inlined.vgtest test_enter_inlined.stderr.exp test_enter_inlined.post.exp \
4254
test_nested_inlined.vgtest test_nested_inlined.stderr.exp test_nested_inlined.post.exp \
55+
test_signal.vgtest test_signal.stderr.exp test_signal.post.exp \
56+
test_exception.vgtest test_exception.stderr.exp test_exception.post.exp \
57+
test_longjmp.vgtest test_longjmp.stderr.exp test_longjmp.post.exp \
58+
test_tailcall.vgtest test_tailcall.stderr.exp test_tailcall.post.exp \
59+
test_recursion.vgtest test_recursion.stderr.exp test_recursion.post.exp \
4360
test_schema.vgtest test_schema.stderr.exp test_schema.post.exp
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "tracegrind.h"
2+
#include <stdexcept>
3+
4+
/*
5+
* Test: C++ exception unwinding through regular (non-inlined) functions.
6+
*
7+
* catcher() calls thrower(), which calls do_throw().
8+
* do_throw() throws an exception that unwinds back through thrower()
9+
* to catcher()'s catch block. Verifies the call stack is properly
10+
* maintained across exception unwinding.
11+
*
12+
* Call chain: catcher -> thrower -> do_throw (throws)
13+
*/
14+
15+
static void __attribute__((noinline)) do_throw(int x) {
16+
if (x > 0)
17+
throw std::runtime_error("boom");
18+
}
19+
20+
static int __attribute__((noinline)) thrower(int n) {
21+
volatile int x = n;
22+
do_throw(x);
23+
return x;
24+
}
25+
26+
static int __attribute__((noinline)) catcher(int n) {
27+
try {
28+
return thrower(n);
29+
} catch (const std::exception&) {
30+
return -1;
31+
}
32+
}
33+
34+
int main() {
35+
volatile int input = 5;
36+
TRACEGRIND_ADD_MARKER("start");
37+
TRACEGRIND_START_INSTRUMENTATION;
38+
int result = catcher(input);
39+
TRACEGRIND_STOP_INSTRUMENTATION;
40+
TRACEGRIND_ADD_MARKER("end");
41+
return result != -1;
42+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Format Version: N
2+
Format Name: tracegrind-msgpack
3+
Schema Version: N
4+
Event Schemas (discriminated union):
5+
0 (MARKER): ['seq', 'tid', 'event', 'marker']
6+
Showing N of N rows
7+
seq=N | tid=1 | event=MARKER | marker=start
8+
seq=N | tid=1 | event=ENTER | fn=catcher(int) | obj=test_exception.bin | file=test_exception.cpp | line=0 | Ir=N
9+
seq=N | tid=1 | event=ENTER | fn=thrower(int) | obj=test_exception.bin | file=test_exception.cpp | line=0 | Ir=N
10+
seq=N | tid=1 | event=ENTER | fn=do_throw(int) | obj=test_exception.bin | file=test_exception.cpp | line=0 | Ir=N
11+
seq=N | tid=1 | event=EXIT | fn=do_throw(int) | obj=test_exception.bin | file=test_exception.cpp | line=0 | Ir=N
12+
seq=N | tid=1 | event=EXIT | fn=thrower(int) | obj=test_exception.bin | file=test_exception.cpp | line=0 | Ir=N
13+
seq=N | tid=1 | event=EXIT | fn=catcher(int) | obj=test_exception.bin | file=test_exception.cpp | line=0 | Ir=N
14+
seq=N | tid=1 | event=MARKER | marker=end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
3+
Events : Ir
4+
Collected :
5+
6+
I refs:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
prereq: ../../tracegrind/scripts/tracegrind-analyzer --help 2>/dev/null
2+
prog: test_exception.bin
3+
vgopts: --tracegrind-out-file=tracegrind.out.test_exception --instr-atstart=no
4+
post: ../../tracegrind/scripts/tracegrind-analyzer tracegrind.out.test_exception.msgpack.lz4 | ./filter_trace | grep -E '(Format |Schema |Event Schemas|Showing |MARKER|fn=catcher|fn=thrower|fn=do_throw)'
5+
cleanup: rm -f tracegrind.out.test_exception.msgpack.lz4

tracegrind/tests/test_longjmp.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "tracegrind.h"
2+
#include <setjmp.h>
3+
4+
/*
5+
* Test: longjmp unwinding multiple call frames.
6+
*
7+
* outer() calls middle(), which calls inner().
8+
* inner() does longjmp back to outer(), skipping middle()'s return.
9+
* Verifies tracegrind properly unwinds the call stack on non-local jumps.
10+
*
11+
* Call chain: outer -> middle -> inner (longjmp back to outer)
12+
*/
13+
14+
static jmp_buf env;
15+
16+
static void __attribute__((noinline)) inner(int n) {
17+
volatile int x = n * 2;
18+
(void)x;
19+
longjmp(env, 42);
20+
}
21+
22+
static void __attribute__((noinline)) middle(int n) {
23+
volatile int x = n + 1;
24+
inner(x);
25+
/* never reached */
26+
x = x + 1;
27+
}
28+
29+
static int __attribute__((noinline)) outer(int n) {
30+
int val = setjmp(env);
31+
if (val == 0) {
32+
middle(n);
33+
/* never reached */
34+
return -1;
35+
}
36+
return val;
37+
}
38+
39+
int main(void) {
40+
volatile int input = 5;
41+
TRACEGRIND_ADD_MARKER("start");
42+
TRACEGRIND_START_INSTRUMENTATION;
43+
int result = outer(input);
44+
TRACEGRIND_STOP_INSTRUMENTATION;
45+
TRACEGRIND_ADD_MARKER("end");
46+
return result != 42;
47+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Format Version: N
2+
Format Name: tracegrind-msgpack
3+
Schema Version: N
4+
Event Schemas (discriminated union):
5+
0 (MARKER): ['seq', 'tid', 'event', 'marker']
6+
Showing N of N rows
7+
seq=N | tid=1 | event=MARKER | marker=start
8+
seq=N | tid=1 | event=ENTER | fn=outer | obj=test_longjmp.bin | file=test_longjmp.c | line=0 | Ir=N
9+
seq=N | tid=1 | event=ENTER | fn=middle | obj=test_longjmp.bin | file=test_longjmp.c | line=0 | Ir=N
10+
seq=N | tid=1 | event=ENTER | fn=inner | obj=test_longjmp.bin | file=test_longjmp.c | line=0 | Ir=N
11+
seq=N | tid=1 | event=EXIT | fn=inner | obj=test_longjmp.bin | file=test_longjmp.c | line=0 | Ir=N
12+
seq=N | tid=1 | event=EXIT | fn=middle | obj=test_longjmp.bin | file=test_longjmp.c | line=0 | Ir=N
13+
seq=N | tid=1 | event=EXIT | fn=outer | obj=test_longjmp.bin | file=test_longjmp.c | line=0 | Ir=N
14+
seq=N | tid=1 | event=MARKER | marker=end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
3+
Events : Ir
4+
Collected :
5+
6+
I refs:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
prereq: ../../tracegrind/scripts/tracegrind-analyzer --help 2>/dev/null
2+
prog: test_longjmp.bin
3+
vgopts: --tracegrind-out-file=tracegrind.out.test_longjmp --instr-atstart=no
4+
post: ../../tracegrind/scripts/tracegrind-analyzer tracegrind.out.test_longjmp.msgpack.lz4 | ./filter_trace | grep -E '(Format |Schema |Event Schemas|Showing |MARKER|fn=outer|fn=middle|fn=inner)'
5+
cleanup: rm -f tracegrind.out.test_longjmp.msgpack.lz4

tracegrind/tests/test_recursion.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "tracegrind.h"
2+
3+
/*
4+
* Test: deep recursion (100 levels).
5+
*
6+
* recurse() calls itself 100 times, then returns back through
7+
* all frames. Verifies the call stack handles deep nesting and
8+
* produces balanced ENTER/EXIT pairs.
9+
*/
10+
11+
static int __attribute__((noinline)) recurse(int depth) {
12+
volatile int d = depth;
13+
if (d <= 0)
14+
return 0;
15+
return recurse(d - 1) + 1;
16+
}
17+
18+
int main(void) {
19+
volatile int input = 100;
20+
TRACEGRIND_ADD_MARKER("start");
21+
TRACEGRIND_START_INSTRUMENTATION;
22+
int result = recurse(input);
23+
TRACEGRIND_STOP_INSTRUMENTATION;
24+
TRACEGRIND_ADD_MARKER("end");
25+
return result != 100;
26+
}

0 commit comments

Comments
 (0)