Skip to content

Commit f2334e7

Browse files
art049claude
andcommitted
feat(tracegrind): proper nested inline tracking via InlIPCursor stack diffing
Replace flat single-pointer inline tracking with a per-BB inline call stack built via Valgrind's InlIPCursor API. BB-to-BB transitions now diff the old and new inline stacks to emit the minimal EXIT/ENTER sequence, producing correct containment (ENTER outer → ENTER inner → EXIT inner → EXIT outer) instead of flat transitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 39d1346 commit f2334e7

7 files changed

Lines changed: 81 additions & 32 deletions

File tree

tracegrind/bb.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ static BB* new_bb(obj_node* obj, PtrdiffT offset,
143143
bb->fn = 0;
144144
bb->line = 0;
145145
bb->is_entry = 0;
146-
bb->inl_fn = NULL;
146+
bb->inl_fns = NULL;
147+
bb->inl_depth = 0;
147148
bb->bbcc_list = 0;
148149
bb->last_bbcc = 0;
149150

@@ -332,6 +333,8 @@ void TG_(delete_bb)(Addr addr)
332333
if (bb->bbcc_list == 0) {
333334
/* can be safely deleted */
334335

336+
if (bb->inl_fns) VG_(free)(bb->inl_fns);
337+
335338
/* Fill the block up with junk and then free it, so we will
336339
hopefully get a segfault if it is used again by mistake. */
337340
size = sizeof(BB)

tracegrind/bbcc.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -831,17 +831,37 @@ void TG_(setup_bbcc)(BB* bb)
831831

832832
TG_(current_state).bbcc = bbcc;
833833

834-
/* Check for inline function transition */
834+
/* Check for inline function transitions */
835835
if (TG_(current_state).collect) {
836836
thread_info* ti = TG_(get_current_thread)();
837-
if (ti && bb->inl_fn != ti->cur_inl_fn) {
838-
if (ti->cur_inl_fn != NULL) {
839-
TG_(trace_emit_exit_inlined)(TG_(current_tid), bb, ti->cur_inl_fn);
837+
if (ti) {
838+
UInt old_depth = ti->cur_inl_depth;
839+
UInt new_depth = bb->inl_depth;
840+
841+
/* Fast path: both empty (most BBs) */
842+
if (old_depth != 0 || new_depth != 0) {
843+
/* Find longest common prefix */
844+
UInt common = 0;
845+
UInt min_depth = old_depth < new_depth ? old_depth : new_depth;
846+
while (common < min_depth &&
847+
ti->cur_inl_fns[common] == bb->inl_fns[common])
848+
common++;
849+
850+
/* EXIT from deepest down to common level */
851+
for (Int i = (Int)old_depth - 1; i >= (Int)common; i--)
852+
TG_(trace_emit_exit_inlined)(TG_(current_tid), bb,
853+
ti->cur_inl_fns[i]);
854+
855+
/* ENTER from common level up to new deepest */
856+
for (UInt i = common; i < new_depth; i++)
857+
TG_(trace_emit_enter_inlined)(TG_(current_tid), bb,
858+
bb->inl_fns[i]);
859+
860+
/* Update thread state */
861+
for (UInt i = 0; i < new_depth; i++)
862+
ti->cur_inl_fns[i] = bb->inl_fns[i];
863+
ti->cur_inl_depth = new_depth;
840864
}
841-
if (bb->inl_fn != NULL) {
842-
TG_(trace_emit_enter_inlined)(TG_(current_tid), bb);
843-
}
844-
ti->cur_inl_fn = bb->inl_fn;
845865
}
846866
}
847867

tracegrind/callstack.c

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,14 @@ void TG_(push_call_stack)(BBCC* from, UInt jmp, BBCC* to, Addr sp, Bool skip)
234234

235235
/* Emit trace sample on function entry */
236236
if (!skip && TG_(current_state).collect) {
237-
/* Emit EXIT_INLINED if we're entering a new function while inside inlined code */
237+
/* Exit entire inline stack, deepest first */
238238
thread_info* ti = TG_(get_current_thread)();
239-
if (ti && ti->cur_inl_fn != NULL && TG_(current_state).bbcc) {
240-
TG_(trace_emit_exit_inlined)(TG_(current_tid),
241-
TG_(current_state).bbcc->bb,
242-
ti->cur_inl_fn);
243-
ti->cur_inl_fn = NULL;
239+
if (ti && ti->cur_inl_depth > 0 && TG_(current_state).bbcc) {
240+
for (Int i = (Int)ti->cur_inl_depth - 1; i >= 0; i--)
241+
TG_(trace_emit_exit_inlined)(TG_(current_tid),
242+
TG_(current_state).bbcc->bb,
243+
ti->cur_inl_fns[i]);
244+
ti->cur_inl_depth = 0;
244245
}
245246
fn_node* to_fn = to->cxt->fn[0];
246247
TG_(trace_emit_sample)(TG_(current_tid), True, to_fn);
@@ -343,13 +344,14 @@ void TG_(pop_call_stack)(void)
343344

344345
/* Emit trace sample on function exit */
345346
if (TG_(current_state).collect) {
346-
/* Emit EXIT_INLINED if we're leaving while inside inlined code */
347+
/* Exit entire inline stack, deepest first */
347348
thread_info* ti = TG_(get_current_thread)();
348-
if (ti && ti->cur_inl_fn != NULL && TG_(current_state).bbcc) {
349-
TG_(trace_emit_exit_inlined)(TG_(current_tid),
350-
TG_(current_state).bbcc->bb,
351-
ti->cur_inl_fn);
352-
ti->cur_inl_fn = NULL;
349+
if (ti && ti->cur_inl_depth > 0 && TG_(current_state).bbcc) {
350+
for (Int i = (Int)ti->cur_inl_depth - 1; i >= 0; i--)
351+
TG_(trace_emit_exit_inlined)(TG_(current_tid),
352+
TG_(current_state).bbcc->bb,
353+
ti->cur_inl_fns[i]);
354+
ti->cur_inl_depth = 0;
353355
}
354356
TG_(trace_emit_sample)(TG_(current_tid), False, to_fn);
355357
}

tracegrind/dump.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ void TG_(trace_emit_sample)(ThreadId tid, Bool is_enter,
453453
deltas, es->size);
454454
}
455455

456-
void TG_(trace_emit_enter_inlined)(ThreadId tid, BB* bb)
456+
void TG_(trace_emit_enter_inlined)(ThreadId tid, BB* bb, const HChar* inl_fn)
457457
{
458458
Int i;
459459

@@ -473,7 +473,7 @@ void TG_(trace_emit_enter_inlined)(ThreadId tid, BB* bb)
473473

474474
TG_(trace_out).seq++;
475475

476-
const HChar* fn_name = bb->inl_fn;
476+
const HChar* fn_name = inl_fn;
477477
const HChar* obj_name = bb->obj ? bb->obj->name : "???";
478478
const HChar* file_name = (bb->fn && bb->fn->file) ? bb->fn->file->name : "???";
479479
UInt line = bb->line;

tracegrind/fn.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,11 +594,30 @@ fn_node* TG_(get_fn_node)(BB* bb)
594594

595595
DiEpoch ep = VG_(current_DiEpoch)();
596596

597-
/* Check if BB start address is in inlined code */
597+
/* Build inline stack for this BB using InlIPCursor */
598598
{
599-
const HChar* inl_fn_name = NULL;
600-
VG_(get_inline_fnname)(ep, bb_addr(bb), &inl_fn_name);
601-
bb->inl_fn = inl_fn_name; /* NULL if not inlined */
599+
InlIPCursor* iipc = VG_(new_IIPC)(ep, bb_addr(bb));
600+
if (iipc) {
601+
const HChar* tmp[TG_MAX_INL_DEPTH + 1];
602+
Int total = 0;
603+
do {
604+
const HChar* fn_name = NULL;
605+
VG_(get_fnname_inl)(ep, bb_addr(bb), &fn_name, iipc);
606+
if (fn_name && total < TG_MAX_INL_DEPTH + 1)
607+
tmp[total++] = fn_name;
608+
} while (VG_(next_IIPC)(iipc));
609+
VG_(delete_IIPC)(iipc);
610+
611+
/* tmp[] is innermost-first; last entry is the non-inlined function (skip it) */
612+
Int inl_count = total - 1;
613+
if (inl_count > 0) {
614+
bb->inl_depth = inl_count;
615+
bb->inl_fns = VG_(malloc)("tg.bb.inl", inl_count * sizeof(HChar*));
616+
/* Reverse into outermost-first order */
617+
for (Int i = 0; i < inl_count; i++)
618+
bb->inl_fns[i] = tmp[inl_count - 1 - i];
619+
}
620+
}
602621
}
603622

604623
if (0 == VG_(strcmp)(fnname, "???")) {

tracegrind/global.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
/* Enable experimental features? */
6060
#define TG_EXPERIMENTAL 0
6161

62+
/* Maximum depth of inline call stack tracking */
63+
#define TG_MAX_INL_DEPTH 16
64+
6265

6366
/*------------------------------------------------------------*/
6467
/*--- Command line options ---*/
@@ -301,7 +304,8 @@ struct _BB {
301304
* allocated directly after this struct */
302305
Bool cjmp_inverted; /* is last side exit actually fall through? */
303306

304-
const HChar* inl_fn; /* inlined function name at BB start, or NULL */
307+
const HChar** inl_fns; /* inlined fn names at BB start (outermost first), or NULL */
308+
UInt inl_depth; /* number of entries in inl_fns */
305309

306310
UInt instr_len;
307311
UInt cost_count;
@@ -566,8 +570,9 @@ struct _thread_info {
566570
/* CSV trace: per-thread snapshot of cost at last sample emission */
567571
FullCost last_sample_cost;
568572

569-
/* Inline tracking: current inlined function name (NULL if not in inlined code) */
570-
const HChar* cur_inl_fn;
573+
/* Inline tracking: current inline call stack (outermost first) */
574+
const HChar* cur_inl_fns[TG_MAX_INL_DEPTH];
575+
UInt cur_inl_depth;
571576

572577
/* thread specific data structure containers */
573578
fn_array fn_active;
@@ -738,7 +743,7 @@ void TG_(run_post_signal_on_call_stack_bottom)(void);
738743
void TG_(trace_open_output)(void);
739744
void TG_(trace_reopen_child)(void);
740745
void TG_(trace_emit_sample)(ThreadId tid, Bool is_enter, fn_node* fn);
741-
void TG_(trace_emit_enter_inlined)(ThreadId tid, BB* bb);
746+
void TG_(trace_emit_enter_inlined)(ThreadId tid, BB* bb, const HChar* inl_fn);
742747
void TG_(trace_emit_exit_inlined)(ThreadId tid, BB* bb, const HChar* inl_fn);
743748
void TG_(trace_emit_fork)(ThreadId tid, Int child_pid);
744749
void TG_(trace_emit_marker)(ThreadId tid, const HChar* marker);

tracegrind/tests/test_nested_inlined.post.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ Showing N of N rows
1414
seq=N | tid=1 | event=MARKER | marker=start
1515
seq=N | tid=1 | event=ENTER | fn=caller | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=0 | Ir=N
1616
seq=N | tid=1 | event=ENTER_INLINED | fn=outer_inline | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=26 | Ir=N
17-
seq=N | tid=1 | event=EXIT_INLINED | fn=outer_inline | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=9 | Ir=N
1817
seq=N | tid=1 | event=ENTER_INLINED | fn=inner_inline | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=9 | Ir=N
1918
seq=N | tid=1 | event=EXIT_INLINED | fn=inner_inline | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=9 | Ir=N
19+
seq=N | tid=1 | event=EXIT_INLINED | fn=outer_inline | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=9 | Ir=N
2020
seq=N | tid=1 | event=EXIT | fn=caller | obj=test_nested_inlined.bin | file=test_nested_inlined.c | line=0 | Ir=N
2121
seq=N | tid=1 | event=MARKER | marker=end

0 commit comments

Comments
 (0)