Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
118cde1
feat: fork callgrind as tracegrind (mechanical rename)
art049 Feb 4, 2026
a8f5a9e
feat: replace callgraph dump with streaming CSV trace output
art049 Feb 4, 2026
9211320
feat: add MsgPack+LZ4 output format for tracegrind
art049 Feb 5, 2026
864a751
feat: discriminated union schema for msgpack, drop CSV format
art049 Feb 6, 2026
b4da8df
feat(bench): add tracegrind benchmarks for comparison with callgrind
art049 Feb 6, 2026
38e24d9
fix(bench): skip tracegrind tests when tool not available
art049 Feb 6, 2026
1a04cd7
feat: add MARKER event type and remove legacy dump infrastructure
art049 Feb 6, 2026
cafcab2
refactor: remove dead callgrind remnants from tracegrind
art049 Feb 6, 2026
ea18a5b
refactor: remove --fn-skip, --obj-skip, and --skip-direct-rec options
art049 Feb 6, 2026
c699fd1
feat(tracegrind): add regression tests and CI integration
art049 Feb 6, 2026
39d1346
feat(tracegrind): add ENTER_INLINED/EXIT_INLINED events for inline fu…
art049 Feb 7, 2026
f2334e7
feat(tracegrind): proper nested inline tracking via InlIPCursor stack…
art049 Feb 7, 2026
7d55d1c
test(tracegrind): add control flow regression tests
art049 Feb 7, 2026
7a65b79
feat(tracegrind): add THREAD_CREATE event and rename/reorder event types
art049 Feb 7, 2026
c1f86fa
test(tracegrind): add syscall regression test with collect-systime=nsec
art049 Feb 7, 2026
ef5bcde
feat(tracegrind): add creator and version metadata to output header
art049 Feb 7, 2026
974acae
test(tracegrind): add multi-thread interleaved callstack regression test
art049 Feb 7, 2026
2a7fa27
fix(tracegrind): update test_thread_create expected output for resolv…
art049 Feb 7, 2026
b6449a3
test: improved benchmarks
art049 Feb 7, 2026
c036d1f
feat(tracegrind): add counter_units map to output header
art049 Feb 7, 2026
515925b
ci: refine benchmark timeout
art049 Feb 7, 2026
91d6e85
fix(tracegrind): stop auto-appending .msgpack.lz4 extension to output…
art049 Feb 7, 2026
4c7b519
feat(tracegrind): bump output format from v3 to v4 with top-level cou…
art049 Feb 8, 2026
6717f23
docs(tracegrind): add example output files and generation script
art049 Feb 8, 2026
3d77321
perf(tracegrind): use faster LZ4 compression and cache string lengths
art049 Feb 8, 2026
139e48e
style(tracegrind): apply clang-format and add pre-commit hook
art049 Feb 8, 2026
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
22 changes: 15 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ on:
workflow_dispatch:

jobs:
test-callgrind:
test:
timeout-minutes: 30
strategy:
matrix:
runner:
- platform: ubuntu-22.04
ubuntu-version: 22.04
- platform: ubuntu-24.04
ubuntu-version: 24.04
tool:
- callgrind
- tracegrind

runs-on: ${{ matrix.runner.platform }}

Expand All @@ -32,7 +36,7 @@ jobs:
path-exclude /usr/share/man/*
path-exclude /usr/share/info/*
EOF

- name: Update apt-get cache
run: sudo apt-get update

Expand All @@ -51,6 +55,10 @@ jobs:
docbook-xml \
xsltproc

- name: Install uv
if: matrix.tool == 'tracegrind'
uses: astral-sh/setup-uv@v7

- name: Run autogen
run: ./autogen.sh

Expand All @@ -63,17 +71,17 @@ jobs:
- name: Build test dependencies
run: |
make -C tests arch_test os_test true
make -C callgrind/tests check
make -C ${{ matrix.tool }}/tests check

- name: Run Callgrind tests
- name: Run tests
run: |
cd callgrind/tests
cd ${{ matrix.tool }}/tests
TESTS=$(ls *.vgtest | grep -v bug497723.vgtest)
perl ../../tests/vg_regtest --valgrind=../../vg-in-place $TESTS

- name: Upload test logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: callgrind-test-logs-${{ matrix.runner.ubuntu-version }}
path: callgrind/tests/*.log
name: ${{ matrix.tool }}-test-logs-${{ matrix.runner.ubuntu-version }}
path: ${{ matrix.tool }}/tests/*.log
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
/autom4te.cache
/bin
/cachegrind.out.*
/callgrind.out.*
/tracegrind.out.*
/compile
/config.guess
/config.h*
/config.log
/config.status
/config.sub
/configure
/configure~
/default.supp
/depcomp
/glibc-2.X.supp
Expand Down Expand Up @@ -161,6 +164,31 @@
/callgrind/tests/inline-samefile
/callgrind/tests/inline-crossfile

# /tracegrind/
/tracegrind/*.so
/tracegrind/.deps
/tracegrind/tracegrind-*-darwin
/tracegrind/tracegrind-*-linux
/tracegrind/tracegrind-*-solaris
/tracegrind/tracegrind-*-freebsd
/tracegrind/Makefile
/tracegrind/Makefile.in

# /tracegrind/tests/
/tracegrind/tests/*.dSYM
/tracegrind/tests/*.post.diff*
/tracegrind/tests/*.post.out
/tracegrind/tests/*.stderr.diff*
/tracegrind/tests/*.stderr.out
/tracegrind/tests/*.stdout.diff*
/tracegrind/tests/*.stdout.out
/tracegrind/tests/.deps
/tracegrind/tests/Makefile
/tracegrind/tests/Makefile.in
/tracegrind/tests/tracegrind.out.*
/tracegrind/tests/fibo
/tracegrind/tests/*.bin

# /coregrind/
/coregrind/*.a
/coregrind/*.dSYM
Expand Down
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v14.0.6
hooks:
- id: clang-format
files: ^tracegrind/
types_or: [c, c++]
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ TOOLS = \
memcheck \
cachegrind \
callgrind \
tracegrind \
helgrind \
drd \
massif \
Expand Down
119 changes: 90 additions & 29 deletions bench/bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,33 @@ def __init__(
raise RuntimeError(f"Valgrind not found at: {self.valgrind_path}")
self.valgrind_version = result.stdout.strip()

def run_valgrind(self, *args: str) -> None:
"""Execute valgrind with given arguments.
# Check which tools are available
self.available_tools = self._detect_available_tools()

def _detect_available_tools(self) -> set:
"""Detect which valgrind tools are available."""
tools = set()
for tool in ["callgrind", "tracegrind"]:
result = subprocess.run(
[self.valgrind_path, f"--tool={tool}", "--help"],
capture_output=True,
text=True,
)
if result.returncode == 0:
tools.add(tool)
return tools

def run_valgrind(self, tool: str, *args: str) -> None:
"""Execute valgrind with given tool and arguments.

Args:
tool: Valgrind tool to use (callgrind, tracegrind)
*args: Valgrind arguments
"""

cmd = [
self.valgrind_path,
"--tool=callgrind",
f"--tool={tool}",
"--log-file=/dev/null",
*args,
*shlex.split(self.cmd),
Expand All @@ -75,76 +92,119 @@ def runner(request):
return request.config._valgrind_runner


CACHE_SIM_OPTIONS = [
"--cache-sim=yes",
"--I1=32768,8,64",
"--D1=32768,8,64",
"--LL=8388608,16,64",
]

def pytest_generate_tests(metafunc):
"""Parametrize tests with valgrind configurations."""
if "valgrind_args" in metafunc.fixturenames:
if "tool_and_args" in metafunc.fixturenames:
runner = getattr(metafunc.config, "_valgrind_runner", None)
if not runner:
return

# Define valgrind configurations
configs = [
(["--read-inline-info=no"], "no-inline"),
(["--read-inline-info=yes"], "inline"),
# Define configurations for each tool
# Format: (tool, args, config_name)
all_configs = [
# Callgrind configurations
("callgrind", ["--read-inline-info=no"], "cg/no-inline"),
("callgrind", ["--read-inline-info=yes"], "cg/inline"),
(
"callgrind",
[
*CACHE_SIM_OPTIONS,
"--trace-children=yes",
"--cache-sim=yes",
"--I1=32768,8,64",
"--D1=32768,8,64",
"--LL=8388608,16,64",
"--collect-systime=nsec",
"--compress-strings=no",
"--combine-dumps=yes",
"--dump-line=no",
"--read-inline-info=yes",
],
"full-with-inline",
"cg/full-inline",
),
(
"callgrind",
[
*CACHE_SIM_OPTIONS,
"--trace-children=yes",
"--cache-sim=yes",
"--I1=32768,8,64",
"--D1=32768,8,64",
"--LL=8388608,16,64",
"--collect-systime=nsec",
"--compress-strings=no",
"--combine-dumps=yes",
"--dump-line=no",
"--read-inline-info=no",
],
"cg/full-no-inline",
),
# Tracegrind configurations (only available in codspeed fork)
("tracegrind", ["--read-inline-info=no"], "tg/no-inline"),
("tracegrind", ["--read-inline-info=yes"], "tg/inline"),
(
"tracegrind",
[
*CACHE_SIM_OPTIONS,
"--trace-children=yes",
"--collect-systime=nsec",
"--read-inline-info=no",
],
"full-no-inline",
"tg/full-no-inline",
),
(
"tracegrind",
[
*CACHE_SIM_OPTIONS,
"--trace-children=yes",
"--collect-systime=nsec",
"--read-inline-info=yes",
],
"tg/full-inline",
),
]

# Filter configs to only include available tools
configs = [
(tool, args, name)
for tool, args, name in all_configs
if tool in runner.available_tools
]

if not configs:
return

# If the valgrind version is from CodSpeed, we don't want to display the exact version
# to allow comparison against older versions.
# to allow comparison against older versions.
if ".codspeed" in runner.valgrind_version:
runner.valgrind_version = "valgrind.codspeed"
runner.valgrind_version = "codspeed"
# Clean valgrind version names
else:
runner.valgrind_version.removeprefix("valgrind-")

# Create test IDs with format: valgrind-version, command, config-name
test_ids = [
f"{runner.valgrind_version}, {runner.cmd}, {config_name}"
for _, config_name in configs
f"{runner.valgrind_version}/{config_name}, {runner.cmd}"
for _, _, config_name in configs
]

# Parametrize with just the args
# Parametrize with (tool, args) tuples
metafunc.parametrize(
"valgrind_args",
[args for args, _ in configs],
"tool_and_args",
[(tool, args) for tool, args, _ in configs],
ids=test_ids,
)


@pytest.mark.benchmark
def test_valgrind(runner, valgrind_args):
def test_valgrind(runner, tool_and_args):
if runner:
runner.run_valgrind(*valgrind_args)
tool, args = tool_and_args
runner.run_valgrind(tool, *args)


def main():
parser = argparse.ArgumentParser(
description="Benchmark Valgrind with pytest-codspeed",
description="Benchmark Valgrind tools (callgrind, tracegrind) with pytest-codspeed",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
Expand Down Expand Up @@ -179,6 +239,7 @@ def main():
valgrind_path=args.valgrind_path,
)
print(f"Valgrind version: {runner.valgrind_version}")
print(f"Available tools: {', '.join(sorted(runner.available_tools))}")
print(f"Command: {args.cmd}")

# Plugin to pass runner to tests
Expand All @@ -187,7 +248,7 @@ def pytest_configure(self, config):
config._valgrind_runner = runner

exit_code = pytest.main(
[__file__, "-v", "--codspeed", "--codspeed-warmup-time=0", "--codspeed-max-time=5"],
[__file__, "-v", "--codspeed", "--codspeed-warmup-time=0", "--codspeed-max-time=30"],
plugins=[RunnerPlugin()],
)
if exit_code != 0 and exit_code != 5:
Expand Down
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5807,6 +5807,8 @@ AC_CONFIG_FILES([
callgrind/callgrind_annotate
callgrind/callgrind_control
callgrind/tests/Makefile
tracegrind/Makefile
tracegrind/tests/Makefile
helgrind/Makefile
helgrind/tests/Makefile
drd/Makefile
Expand Down
Loading
Loading