Repository navigation
Conversation
get_inline_info() missed the debug cache for an address whose entry belonged to another address, then stored its inline name in that entry anyway, under the other address's key. A later lookup of the other address returned the wrong inline function. The cache is shared by all functions of a dump, and they are dumped in an order derived from pointer values, so which cfni= markers a function got depended on the tool binary's layout rather than on the profiled program. On a miss, query the inline name without caching it; the entry is only filled by get_debug_pos(), which writes the whole entry. Also compare inline function names by content rather than by pointer, since the same name is not always the same pointer. Closes COD-3536 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merging this PR will not alter performance
Comparing Footnotes
|
The dump comparator ordered BBCCs by subtracting pointers to separately allocated obj, file and fn nodes. That is undefined, and since the node sizes are not powers of two the compiler's exact division returns arbitrary values, so the comparison was not a consistent order. qsort could then split one function's BBCCs into several blocks, each with its own fn= header. A block boundary falling inside a function changed the markers written around it, for instance dropping the cfni= marker that closes an inlined region. Which functions got split depended on heap addresses, so it varied with the tool binary and the loaded debug info. Compare the nodes' creation numbers instead, and return -1/0/1 rather than a difference that can overflow an int. The dump order is now also independent of where the nodes are allocated. Closes COD-3536 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| if (cxt1->fn[0]->file->obj != cxt2->fn[0]->file->obj) | ||
| return cxt1->fn[0]->file->obj - cxt2->fn[0]->file->obj; | ||
| return CMP3(cxt1->fn[0]->file->obj->number, | ||
| cxt2->fn[0]->file->obj->number); | ||
|
|
||
| if (cxt1->fn[0]->file != cxt2->fn[0]->file) | ||
| return cxt1->fn[0]->file - cxt2->fn[0]->file; | ||
| return CMP3(cxt1->fn[0]->file->number, cxt2->fn[0]->file->number); | ||
|
|
||
| if (cxt1->fn[0] != cxt2->fn[0]) | ||
| return cxt1->fn[0] - cxt2->fn[0]; | ||
| return CMP3(cxt1->fn[0]->number, cxt2->fn[0]->number); |
There was a problem hiding this comment.
Function grouping lacks direct coverage. This comparator fixes a heap-layout-dependent failure by keeping a function’s BBCCs together, but no regression test checks that grouping directly. The existing inline test reads only the first
fn=main block, so a future split could pass unnoticed. A test that checks block contiguity would protect the behavior this change repairs.
Knowledge Base Used: Execution profiling tools
Prompt To Fix With AI
This is a comment left during a code review.
Path: callgrind/dump.c
Line: 998-1006
Comment:
**Function grouping lacks direct coverage.** This comparator fixes a heap-layout-dependent failure by keeping a function’s BBCCs together, but no regression test checks that grouping directly. The existing inline test reads only the first `fn=main` block, so a future split could pass unnoticed. A test that checks block contiguity would protect the behavior this change repairs.
**Knowledge Base Used:** [Execution profiling tools](https://app.greptile.com/codspeed/-/custom-context/knowledge-base/codspeedhq/valgrind-codspeed/-/docs/execution-profiling-tools.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Fix callgrind output that depended on where the tool's nodes were allocated in memory. This made
callgrind/tests/inline-crossfilefail on the ubuntu-22.04 job:Root cause: an inconsistent sort comparator
Before dumping,
my_cmpsorts BBCCs by subtracting pointers toobj_node,file_nodeandfn_node(return cxt1->fn[0]->file - cxt2->fn[0]->file;). These nodes are allocated separately, so the subtraction is undefined. With node sizes that are not a power of two (file_nodeis 728 bytes), GCC compiles it as an exact division: a shift, then a multiply by the modular inverse. That gives arbitrary values whenever the byte distance is not a multiple of the size:The order is not even transitive, so qsort can split one function's BBCCs into several blocks, each with its own
fn=header. In the failing runs,mainwas split right after the inlinedcompute_productcode. The flush at the end of a block writesfe=back to the function's file without checking inline state, so thecfni=???that closes the inlined range was never written.filter_inlineonly reads the firstfn=mainblock, which is exactly the diff above.Which functions get split depends on heap addresses, so it varied with the tool binary, the checkout path and the debug info Valgrind loads. On the ubuntu-22.04 runner, the CI's
apt-get install gdbalso pulls inlibc6-dbg, so Valgrind reads glibc's full DWARF there, which changes the allocations. The test binary was byte-identical between passing and failing runs.Changes
68fea9cdb): compare the nodes' creation numbers, which are unique per obj/file/fn, and return -1/0/1 through aCMP3macro instead of a difference that can overflow anint. This also coversrec_indexandbb->offset.fn=blocks get the same totals.cc0000d96): this is a real bug, but not the cause of the flake. On a cache miss,get_inline_info()stored the inline name in an entry still keyed by another address, so a later lookup of that address got the wrong inline function. On a miss it now queries without caching; entries are only filled byget_debug_pos(), which writes the key, file, line and inline name together. Inline function names are also compared withVG_(strcmp), since the same name is not always the same pointer.The issue also suspected that
last_inline_fnis never reset between functions. It is reset:print_bbccs_of_threadresets it wheneverprint_fn_posreports a new function. So that part is left as is.Verification
I reproduced the failure in an
ubuntu:22.04container, withlibc6-dbginstalled and the repo at the runner's checkout path. Each run below profiles 4 test programs (inline-crossfile,inline-samefile,simwork,threads) under 10GLIBC_TUNABLESCPU-feature settings, and counts functions that appear in more than onefn=block:With the fix, the full callgrind suite passes in that container, and the ubuntu-22.04
test-callgrindjob is green here.Closes COD-3536