Skip to content

About

Automatic compiler-based function tracing for C/C++ (Clang/LLVM): enter/exit, parameters, and return values injected at build time - zero source changes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

FunctionTraceDemo 🐾

Automatic, compiler-based function tracing for C/C++ — add enter/exit, parameters, and return values at build time, with zero changes to your source. A Windows + WSL proof of concept using Clang/LLVM's -finstrument-functions, a custom LLVM pass, and a libclang descriptor generator, built up in four comparable levels.

ENTER main  main.cpp:47
  ENTER Factorial  mathutil.cpp:3
    n = 5
    ENTER Factorial  mathutil.cpp:3
      n = 4
      ... (recursion)
      return = 24
    return = 120
  ENTER ConfigureSpi  device.cpp:11
    cfg = SpiConfig { clock = Spi0 (1), clockHz = 8000000, enabled = true }
  EXIT  ConfigureSpi
EXIT  main

Ordinary code like int Factorial(int n) — no macros, no manual Enter()/Exit() calls — is traced automatically because the build injects the instrumentation.

Why

Adding tracing to a large C/C++ codebase by hand (macros in every function) is invasive and never complete. Clang/LLVM can do it for you at compile time. This project proves that out and compares the practical options, from a one-flag approach to a full LLVM pass that recovers parameter names, values, enum names, and struct fields — none of which require touching application source.

It's a learning/bring-up reference aimed at an eventual embedded LLVM/Clang toolchain, not an always-on production tracer.

Usage

Prerequisites: Clang/LLVM (winget install LLVM.LLVM), CMake ≥ 3.20, Ninja. The LLVM pass (levels 2 / 1+2) needs LLVM dev libraries, so it builds in WSL (sudo apt install clang-14 llvm-14-dev cmake ninja-build) — which also mirrors a real Linux-hosted toolchain.

Build everything and regenerate all sample outputs in one shot:

.\build_all.ps1            # all levels: Windows builds + WSL pass builds
.\build_all.ps1 -SkipWsl   # Windows-only levels (0, 1, 2-signatures)

Or the minimal starting point — a self-contained 3-file level-1 example:

cd examples\hello_trace
cmake -S . -B build-trace -G Ninja -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" -DENABLE_FUNCTION_TRACE=ON
cmake --build build-trace
.\build-trace\hello.exe

How it works

Four levels of capability, each with checked-in sample output in samples/:

Level Technique Adds
none ordinary build app output only
1 -finstrument-functions + runtime enter/exit, name, file:line, depth, timestamp, thread id
2 custom LLVM pass (-fpass-plugin) parameter names, values, types, return values, enum names, struct fields
1+2 both combined -finstrument enter/exit skeleton enriched with the pass's argument data

The compiler injects __cyg_profile_func_enter/exit (level 1) or, for level 2, our LLVM pass rewrites the IR to emit typed logging calls — reading parameter names from debug info and argument values directly from the IR, still with no source edits. A libclang tool generates an enum/struct descriptor table so clockId prints as Spi0 (1) and structs print field-by-field.

Two recursion hazards are handled so tracing doesn't eat itself: the runtime is compiled without instrumentation and marked no_instrument_function, and a thread-local reentrancy guard stops the output path (whose inlined CRT printf is itself instrumented on Windows) from recursing into the hooks.

Full write-ups:

  • docs/APPROACHES.md — compare/contrast of all four levels and the technique tradeoffs (-finstrument-functions vs LLVM pass vs Clang AST).
  • docs/INTEGRATION.md — step-by-step guide to applying 1 / 2 / 1+2 to another project, with a troubleshooting table.

Layout

FunctionTraceDemo/
    build_all.ps1          # one-shot: builds every level (Windows + WSL), regenerates samples
    src/                   # tricky-flow demo app (recursion, virtual dispatch, callbacks, enum + struct params)
    trace/                 # level-1 runtime: __cyg_profile_func_enter/exit + DbgHelp (Windows)
    pass/                  # level-2 LLVM pass + its runtime + build script (WSL)
    tools/trace_meta_gen/  # libclang tool: emits the enum/struct descriptor table
    generated/             # trace_descriptors.cpp (checked-in generated artifact)
    examples/hello_trace/  # minimal 3-file level-1 starter to copy into a new project
    docs/                  # APPROACHES.md, INTEGRATION.md
    samples/               # checked-in output for every level

Contributing

This is a personal tool, published as-is — issues and pull requests aren't accepted (PRs auto-close). Fork it and make it your own. 🐾

License

MIT — see LICENSE. © 2026 RelentlessOldMan.

About

Automatic compiler-based function tracing for C/C++ (Clang/LLVM): enter/exit, parameters, and return values injected at build time - zero source changes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages