This file provides context for AI agents when working on the bpftrace codebase.
bpftrace is a high-level tracing language for Linux eBPF. It uses LLVM as a compiler backend to generate BPF bytecode, and libbpf/bcc for kernel interaction. The language prioritizes conciseness, readability, and clean abstraction over eBPF complexity. Written in C++20.
See docs/developers.md for full build
instructions. Before running any tests, verify the build/ directory and
test binaries exist. If they don't, build first.
Quick reference (Nix, preferred):
nix develop # enter dev shell
cmake -B build -DCMAKE_BUILD_TYPE=Debug
make -C build -j$(nproc)
The built binary is at build/src/bpftrace.
All contributions must not break existing tests and should add new tests when relevant. See tests/README.md for the full test reference (test types, directives, runtime variables, etc.).
Quick reference:
./build/tests/bpftrace_test # unit tests
./build/tests/bpftrace_test --gtest_filter='Parser.*' # filtered unit tests
sudo ./build/tests/self-tests.sh # self tests
sudo ./build/tests/runtime-tests.sh # runtime tests
sudo ./build/tests/runtime-tests.sh --filter="^uprobe" # filtered runtime tests
sudo ./build/tests/tools-parsing-test.sh # tool parsing tests
- C++ code:
clang-formatwith the repo's.clang-formatconfig. Rungit clang-format upstream/masterto format changed code. Every commit must be correctly formatted (no separate "fix formatting" commits). - bpftrace scripts (
.btfiles): format withbpftrace --fmtorscripts/bpftrace_tidy.sh.
- C++ code: Run this script which will fix clang tidy issues:
./scripts/clang_tidy.sh -u - C++20 standard, 80-column line limit, 2-space indentation.
- Variables:
snake_case. Private class members get a trailing underscore (my_var_). Public members and struct fields do not. - Structs: passive data only, all public fields, no methods.
- Classes: everything else.
- Comments: prefer C++ style (
//) over C style (/* */). - Defer to the C++ Core Guidelines for anything not specified here.
- Recoverable errors: return values (
Result,std::optional,int,bool). Do NOT use exceptions for recoverable errors.
Use the LOG() macro with these levels:
DEBUG- always logs (includes file/line info)V1- only with-vflag; for common warnings like "BTF not available"HINT- tips to resolve a problem (use after WARNING or ERROR)WARNING- issues that allow execution to continueERROR- user errors causing exit; primarily used inmain.cppBUG- aborts; indicates internal/unexpected issues (not user errors)
The compilation pipeline:
- Parser (
src/parser.cpp,src/parser.h) - recursive descent parser, generates AST - AST passes (
src/ast/passes/) - semantic analysis, type checking, AST replacement, macro expansion - Codegen (
src/ast/passes/codegen_llvm.cpp,src/ast/irbuilderbpf.cpp) - LLVM IR generation - BPF bytecode (
src/bpfbytecode.cpp) - compiled BPF programs - Runtime (
src/bpftrace.cpp,src/attached_probe.cpp) - probe attachment and event handling
Key directories:
src/ast/- AST node definitions, visitors, pass managersrc/ast/passes/- all compiler passes (type_checker, codegen_llvm, field_analyser, etc.)src/stdlib/- bpftrace standard librarysrc/btf/- BTF (BPF Type Format) supporttests/- unit tests (GoogleTest), self tests, runtime teststools/- example bpftrace tools/scriptsdocs/- developer and language documentation
- Prefer user experience elegance over implementation elegance.
- Prefer boring, verbose, removable code over clever premature abstractions.
- Substantial or breaking changes require the RFC process (see CONTRIBUTING.md).
- Performance of BPF programs and runtime is on the critical path; elsewhere, simplicity and clarity matter more than performance.
- One commit per PR (squash + rebase, no merge commits).
- Commit titles should be changelog-worthy.
- User-impacting changes require a
CHANGELOG.mdentry. - Language changes must update
docs/language.md,docs/stdlib.md, orman/adoc/bpftrace.adoc. To regenerate thedocs/stdlib.mdrun./scripts/generate_stdlib_docs.py - New behavior must be covered by tests.