From a33bef559d9efc93bad66934fbbcca9fb2ebbff6 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Wed, 20 May 2026 11:37:54 +0200 Subject: [PATCH 1/3] refactor(debuginfo): use find_rx_mapping for fallback DI lookup The fallback added in 938e424b1 used VG_(am_find_nsegment) + VG_(am_get_filename) + strcmp against each DI's fsm.filename. Replace it with ML_(find_rx_mapping)(di, a, a), which walks the same set of rx mappings already recorded for each DebugInfo and keeps a per-DI single-entry cache (last_rx_map), so the hot path is O(1). No coverage change for BOLT-style binaries: both R-E PT_LOADs are recorded as rx DebugInfoMappings during ELF acceptance, so the loop finds them just as well as the filename match did. Also emit a Vg_DebugMsg trace when the fallback fires (only with -v -v). --- coregrind/m_debuginfo/debuginfo.c | 35 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c index f42782925..97f98b62b 100644 --- a/coregrind/m_debuginfo/debuginfo.c +++ b/coregrind/m_debuginfo/debuginfo.c @@ -2621,21 +2621,28 @@ DebugInfo* VG_(find_DebugInfo) ( DiEpoch ep, Addr a ) optimized binaries: .bolt.org.text + .text + .text.warm + .text.cold live in two separate R-E PT_LOAD segments). The text-range check above only covers the section named ".text", so addresses in the other - executable region are missed and end up attributed to "???". Ask the - address-space manager which file backs this address, and match it to - a DebugInfo by filename. */ + executable region are missed and end up attributed to "???". Match + against the rx mappings recorded for each DebugInfo. find_rx_mapping + keeps a per-DI single-entry cache (last_rx_map) so the hot case is + O(1) after the first hit. */ if (eq_DiEpoch(ep, VG_(current_DiEpoch)())) { - const NSegment* seg = VG_(am_find_nsegment)(a); - const HChar* filename; - if (seg != NULL && (filename = VG_(am_get_filename)(seg)) != NULL) { - for (di = debugInfo_list; di != NULL; di = di->next) { - if (!is_DI_valid_for_epoch(di, ep)) - continue; - if (di->fsm.filename != NULL - && 0 == VG_(strcmp)(di->fsm.filename, filename)) { - return di; - } - } + for (di = debugInfo_list; di != NULL; di = di->next) { + if (!is_DI_valid_for_epoch(di, ep)) + continue; + + const DebugInfoMapping* map = ML_(find_rx_mapping)(di, a, a); + if (map == NULL) + continue; + + if (VG_(clo_verbosity) >= 2) { + VG_(message)(Vg_DebugMsg, + "find_DebugInfo: rx-mapping fallback matched 0x%lx -> " + "DI %p (%s) [avma=0x%lx size=%lu]\n", + a, di, + di->fsm.filename ? di->fsm.filename : "(no filename)", + map->avma, map->size); + } + return di; } } return NULL; From 8250b721c17b5ba0c4a985826e3365588c82b084 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Wed, 20 May 2026 11:38:01 +0200 Subject: [PATCH 2/3] test(callgrind): smoke-test VG_(find_DebugInfo) attribution Add a tiny C program with hot_fn in .text plus warm_fn/cold_fn in orphan executable sections (warm_code/cold_code) that sit in the same R-E LOAD but outside text_avma..text_avma+text_size. The .vgtest compares the resolved ob= basenames against an expected list, verifying callgrind attributes events to the test binary even when execution leaves .text. --- callgrind/tests/Makefile.am | 3 ++- callgrind/tests/find_debuginfo.c | 26 +++++++++++++++++++++++ callgrind/tests/find_debuginfo.post.exp | 3 +++ callgrind/tests/find_debuginfo.stderr.exp | 6 ++++++ callgrind/tests/find_debuginfo.vgtest | 4 ++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 callgrind/tests/find_debuginfo.c create mode 100644 callgrind/tests/find_debuginfo.post.exp create mode 100644 callgrind/tests/find_debuginfo.stderr.exp create mode 100644 callgrind/tests/find_debuginfo.vgtest diff --git a/callgrind/tests/Makefile.am b/callgrind/tests/Makefile.am index 79a076042..b6dc3de89 100644 --- a/callgrind/tests/Makefile.am +++ b/callgrind/tests/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = \ ann1.post.exp ann1.stderr.exp ann1.vgtest \ ann2.post.exp ann2.stderr.exp ann2.vgtest \ clreq.vgtest clreq.stderr.exp \ + find_debuginfo.vgtest find_debuginfo.stderr.exp find_debuginfo.post.exp \ runtime_obj_skip_py.vgtest runtime_obj_skip_py.stderr.exp runtime_obj_skip_py.post.exp \ runtime_obj_skip_py.py runtime_obj_skip_py_shim.c \ bug497723.stderr.exp bug497723.post.exp bug497723.vgtest \ @@ -30,7 +31,7 @@ EXTRA_DIST = \ inline-crossfile.vgtest inline-crossfile.stderr.exp inline-crossfile.stdout.exp inline-crossfile.post.exp \ inline-crossfile-helper1.h inline-crossfile-helper2.h filter_inline -check_PROGRAMS = clreq simwork threads inline-samefile inline-crossfile +check_PROGRAMS = clreq find_debuginfo simwork threads inline-samefile inline-crossfile AM_CFLAGS += $(AM_FLAG_M3264_PRI) AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) diff --git a/callgrind/tests/find_debuginfo.c b/callgrind/tests/find_debuginfo.c new file mode 100644 index 000000000..f610d77bb --- /dev/null +++ b/callgrind/tests/find_debuginfo.c @@ -0,0 +1,26 @@ +/* Exercise VG_(find_DebugInfo) including the filename fallback for + executable code that lives outside the section named ".text". + + Functions placed in orphan sections "warm_code" and "cold_code" end up + in their own ELF sections (PROGBITS, AX) inside the same R-E LOAD as + .text but *outside* di->text_avma/text_size. Calls into them force + VG_(find_DebugInfo) past the primary text-range check and into the + am_find_nsegment + filename match path added in commit 938e424b1. */ + +static int hot_fn(int x) { return x + 1; } + +__attribute__((noinline, section("warm_code"))) +static int warm_fn(int x) { return x * 3 + 7; } + +__attribute__((noinline, section("cold_code"))) +static int cold_fn(int x) { return x ^ 0x5a; } + +int main(void) +{ + int y = 0; + for (int i = 0; i < 1000; i++) + y = hot_fn(y); + y = warm_fn(y); + y = cold_fn(y); + return y & 0xff; +} diff --git a/callgrind/tests/find_debuginfo.post.exp b/callgrind/tests/find_debuginfo.post.exp new file mode 100644 index 000000000..90b650dcc --- /dev/null +++ b/callgrind/tests/find_debuginfo.post.exp @@ -0,0 +1,3 @@ +find_debuginfo +ld-linux-x86-64.so.2 +libc.so.6 diff --git a/callgrind/tests/find_debuginfo.stderr.exp b/callgrind/tests/find_debuginfo.stderr.exp new file mode 100644 index 000000000..d0b7820ae --- /dev/null +++ b/callgrind/tests/find_debuginfo.stderr.exp @@ -0,0 +1,6 @@ + + +Events : Ir +Collected : + +I refs: diff --git a/callgrind/tests/find_debuginfo.vgtest b/callgrind/tests/find_debuginfo.vgtest new file mode 100644 index 000000000..51417fceb --- /dev/null +++ b/callgrind/tests/find_debuginfo.vgtest @@ -0,0 +1,4 @@ +prog: find_debuginfo +vgopts: --compress-strings=no --callgrind-out-file=callgrind.out.find_debuginfo +post: sh -c 'sed -n "s|^ob=.*/||p" callgrind.out.find_debuginfo | grep -v "^vgpreload" | sort -u' +cleanup: rm -f callgrind.out.find_debuginfo From 465be512b1246c7e48ffa8983286826fc3e3567b Mon Sep 17 00:00:00 2001 From: not-matthias Date: Wed, 20 May 2026 12:03:55 +0200 Subject: [PATCH 3/3] ci: re-enable CODSPEED_PERF_ENABLED in walltime benchmarks Drop the explicit CODSPEED_PERF_ENABLED=false override so the CodSpeed action runs with its default perf integration. --- .github/workflows/codspeed.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 3dee317db..928a8e613 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -92,8 +92,6 @@ jobs: - name: Run the benchmarks uses: CodSpeedHQ/action@main continue-on-error: ${{ matrix.valgrind != 'local' }} - env: - CODSPEED_PERF_ENABLED: false with: working-directory: bench mode: walltime