Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion callgrind/callgrind.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ typedef
VG_USERREQ__TOGGLE_COLLECT,
VG_USERREQ__DUMP_STATS_AT,
VG_USERREQ__START_INSTRUMENTATION,
VG_USERREQ__STOP_INSTRUMENTATION
VG_USERREQ__STOP_INSTRUMENTATION,
Comment thread
not-matthias marked this conversation as resolved.
VG_USERREQ__ADD_OBJ_SKIP
} Vg_CallgrindClientRequest;

/* Dump current state of cost centers, and zero them afterwards */
Expand Down Expand Up @@ -126,4 +127,10 @@ typedef
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STOP_INSTRUMENTATION, \
0, 0, 0, 0, 0)

/* Add an object file path to the obj-skip list at runtime. Path matching
is exact (same as --obj-skip=<path> on the command line). */
#define CALLGRIND_ADD_OBJ_SKIP(path) \
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__ADD_OBJ_SKIP, \
path, 0, 0, 0, 0)

#endif /* __CALLGRIND_H */
18 changes: 12 additions & 6 deletions callgrind/clo.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,17 @@ void CLG_(update_fn_config)(fn_node* fn)
}


void CLG_(add_obj_to_skip)(const HChar* obj_name)
{
HChar* dup = VG_(strdup)("cl.clo.aots.1", obj_name);
CLG_(clo).objs_to_skip_count++;
CLG_(clo).objs_to_skip = VG_(realloc)("cl.clo.aots.2",
CLG_(clo).objs_to_skip,
CLG_(clo).objs_to_skip_count * sizeof(HChar*));
CLG_(clo).objs_to_skip[CLG_(clo).objs_to_skip_count - 1] = dup;
Comment thread
not-matthias marked this conversation as resolved.
}


/*--------------------------------------------------------------------*/
/*--- Command line processing ---*/
/*--------------------------------------------------------------------*/
Expand Down Expand Up @@ -431,12 +442,7 @@ Bool CLG_(process_cmd_line_option)(const HChar* arg)
fnc->skip = CONFIG_TRUE;
}
else if VG_STR_CLO(arg, "--obj-skip", tmp_str) {
HChar *obj_name = VG_(strdup)("cl.clo.pclo.1", tmp_str);
CLG_(clo).objs_to_skip_count++;
CLG_(clo).objs_to_skip = VG_(realloc)("cl.clo.pclo.2",
CLG_(clo).objs_to_skip,
CLG_(clo).objs_to_skip_count*sizeof(HChar*));
CLG_(clo).objs_to_skip[CLG_(clo).objs_to_skip_count-1] = obj_name;
CLG_(add_obj_to_skip)(tmp_str);
}

else if VG_STR_CLO(arg, "--dump-before", tmp_str) {
Expand Down
1 change: 1 addition & 0 deletions callgrind/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@ struct event_sets {
void CLG_(set_clo_defaults)(void);
void CLG_(update_fn_config)(fn_node*);
Bool CLG_(process_cmd_line_option)(const HChar*);
void CLG_(add_obj_to_skip)(const HChar* obj_name);
void CLG_(print_usage)(void);
void CLG_(print_debug_usage)(void);

Expand Down
7 changes: 7 additions & 0 deletions callgrind/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,13 @@ Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret)
*ret = 0; /* meaningless */
break;

case VG_USERREQ__ADD_OBJ_SKIP: {
const HChar* path = (const HChar*)args[1];
CLG_(add_obj_to_skip)(path);
*ret = 0;
break;
}

case VG_USERREQ__GDB_MONITOR_COMMAND: {
Bool handled = handle_gdb_monitor_command (tid, (HChar*)args[1]);
if (handled)
Expand Down
12 changes: 12 additions & 0 deletions callgrind/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ EXTRA_DIST = \
ann1.post.exp ann1.stderr.exp ann1.vgtest \
ann2.post.exp ann2.stderr.exp ann2.vgtest \
clreq.vgtest clreq.stderr.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 \
simwork1.vgtest simwork1.stdout.exp simwork1.stderr.exp \
simwork2.vgtest simwork2.stdout.exp simwork2.stderr.exp \
Expand Down Expand Up @@ -38,3 +40,13 @@ inline_samefile_CFLAGS = $(AM_CFLAGS) -O2 -g
inline_crossfile_CFLAGS = $(AM_CFLAGS) -O2 -g

threads_LDADD = -lpthread

# Shim loaded by runtime_obj_skip_py.py via ctypes. Built unconditionally;
# the test's prereq skips it if the .so is missing.
check_DATA = runtime_obj_skip_py_shim.so

runtime_obj_skip_py_shim.so: runtime_obj_skip_py_shim.c
$(CC) -shared -fPIC -O2 -I$(top_srcdir) -I$(top_srcdir)/include \
$< -o $@
Comment thread
not-matthias marked this conversation as resolved.

CLEANFILES = runtime_obj_skip_py_shim.so
1 change: 1 addition & 0 deletions callgrind/tests/runtime_obj_skip_py.post.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OK
70 changes: 70 additions & 0 deletions callgrind/tests/runtime_obj_skip_py.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Resolve libpython at runtime via sysconfig (mirrors pytest-codspeed's
approach in instruments/walltime.py), register it for obj-skip via the
client-request shim, then turn instrumentation on and run a small
integer workload.

We pass both the sysconfig path AND its os.path.realpath because
callgrind stores the realpath in obj_node->name (after symlink
resolution), and the runtime obj-skip check uses exact strcmp."""

import ctypes
import os
import sys
import sysconfig


def libpython_candidates() -> list[str]:
ldlibrary = sysconfig.get_config_var("LDLIBRARY")
libdir = sysconfig.get_config_var("LIBDIR")
paths: list[str] = []
if ldlibrary and libdir:
paths.append(os.path.join(libdir, ldlibrary))
if ldlibrary:
paths.append(os.path.join(sys.prefix, "lib", ldlibrary))
# Add realpath variants so the exact-match obj-skip finds the
# file under whichever name the loader actually mapped.
resolved: list[str] = []
seen: set[str] = set()
for p in paths:
if not p:
continue
if p not in seen and os.path.exists(p):
resolved.append(p)
seen.add(p)
try:
r = os.path.realpath(p)
except OSError:
continue
if r not in seen and os.path.exists(r):
resolved.append(r)
seen.add(r)
return resolved


def main() -> None:
here = os.path.dirname(os.path.abspath(__file__))
shim = ctypes.CDLL(os.path.join(here, "runtime_obj_skip_py_shim.so"))
shim.add_obj_skip.argtypes = [ctypes.c_char_p]
shim.add_obj_skip.restype = None
shim.start_instr.argtypes = []
shim.start_instr.restype = None
shim.stop_instr.argtypes = []
shim.stop_instr.restype = None

for path in libpython_candidates():
shim.add_obj_skip(path.encode())
if sys.executable:
shim.add_obj_skip(sys.executable.encode())
real = os.path.realpath(sys.executable)
if real != sys.executable:
shim.add_obj_skip(real.encode())

shim.start_instr()
acc = 0
for i in range(10_000):
acc = (acc + i * i) ^ (i << 1)
shim.stop_instr()


if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions callgrind/tests/runtime_obj_skip_py.stderr.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@


Events : Ir
Collected :

I refs:
6 changes: 6 additions & 0 deletions callgrind/tests/runtime_obj_skip_py.vgtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
prereq: command -v python3 >/dev/null && test -f runtime_obj_skip_py_shim.so
Comment thread
not-matthias marked this conversation as resolved.
prog-asis: python3
args: runtime_obj_skip_py.py
vgopts: --instr-atstart=no --compress-strings=no --callgrind-out-file=callgrind.out.runtime_obj_skip_py
post: sh -c 'c=$(awk "/^ob=/{p=(\$0~/libpython/)} /^fn=/&&p{c++} END{print c+0}" callgrind.out.runtime_obj_skip_py); if [ "$c" -lt 100 ]; then echo OK; else echo "FAIL libpython fns=$c"; fi'
cleanup: rm -f callgrind.out.runtime_obj_skip_py
20 changes: 20 additions & 0 deletions callgrind/tests/runtime_obj_skip_py_shim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* Shim so Python (via ctypes) can issue callgrind client requests.
The requests themselves are inline asm and unreachable from pure
Python; this file just wraps them in regular C functions. */

#include "../callgrind.h"

void add_obj_skip(const char* path)
{
CALLGRIND_ADD_OBJ_SKIP(path);
}

void start_instr(void)
{
CALLGRIND_START_INSTRUMENTATION;
}

void stop_instr(void)
{
CALLGRIND_STOP_INSTRUMENTATION;
}
21 changes: 21 additions & 0 deletions coregrind/m_debuginfo/debuginfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -2617,6 +2617,27 @@ DebugInfo* VG_(find_DebugInfo) ( DiEpoch ep, Addr a )
return di;
}
}
/* Fallback for ELFs with multiple executable LOAD segments (e.g. BOLT-
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. */
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;
}
}
Comment on lines +2626 to +2638
Comment thread
not-matthias marked this conversation as resolved.
}
}
return NULL;
}

Expand Down
Loading