The BCC tools: the ready-made suite
BCC ships the ~100 ready-made tracers whose names recur in every cross-check — execsnoop, biolatency, runqlat, tcpconnect, profile. This closing field-guide chapter tours them, explains the two generations (classic Python+C that compiles at runtime versus precompiled libbpf-tools CO-RE binaries), and drives them from Python: resolve a tool, run it, parse its columns into a summary. Several working examples, plus a minimal BCC program to show what a tool is underneath.
The last stop in the field guide is the toolkit that started it all. BCC (the
BPF Compiler Collection) ships roughly a hundred ready-made tracers, and their
names have appeared in nearly every “Cross-check” section of this book —
execsnoop, opensnoop, biolatency, runqlat, tcpconnect, profile. They
are the off-the-shelf instruments you reach for first to confirm what an Aya
program reports, and most are themselves Python, so driving them from Python is
natural: resolve the tool, run it, parse its columns, summarize. This chapter
tours the suite, explains its two generations, and gives several working wrappers
— then shows what a BCC tool actually is underneath.
The code is in examples/66-bcc-tools/. ./demo.sh resolves and runs several
tools through a Python summarizer; the README.md has the details.
Two generations of BCC
“BCC” means two related things today, and the difference is exactly the CO-RE story from Chapter 58:
- Classic BCC — each tool is a Python script with an inline C BPF program
that BCC compiles at runtime with Clang/LLVM against the running kernel’s
headers, then loads. Maximum flexibility, but it carries heavy dependencies:
clang,llvm, and kernel headers matching the running kernel must be present, or you get the infamousFailed to compile BPF text. On Fedora these live inbcc-tools, installed under/usr/share/bcc/tools/(on Debian they’re suffixed-bpfccand on$PATH). - libbpf-tools — the same tools rewritten as precompiled C binaries using
libbpf and CO-RE: compiled once, they relocate against the target kernel’s
BTF at load time, so they need no Clang, no headers, no Python at runtime —
just the binary. Fedora packages these as
libbpf-tools. This is the modern direction, and the same portability argument that motivates shipping an Aya binary rather than compiling on each host.
Knowing which you’re running explains both the dependency footprint and why the libbpf-tools version starts faster and survives a header-less production image.
The suite, grouped
A working map of the tools you’ll actually reach for, several of which mirror chapters you built in Aya:
| Area | Tools | Mirrors |
|---|---|---|
| Process / syscall | execsnoop, opensnoop, statsnoop, syscount, killsnoop |
Ch 9, 11, 38 |
| Scheduler / CPU | runqlat, runqlen, profile, offcputime |
Ch 21, 23 |
| Block / filesystem | biolatency, biosnoop, cachestat, filetop, *slower |
Ch 24, 25 |
| Network | tcpconnect, tcpaccept, tcplife, tcpretrans |
Ch 27, 28 |
| Memory / generic | memleak, funccount, funclatency, stackcount, trace, argdist |
Ch 18, 23 |
The generic four — funccount, funclatency, stackcount, trace,
argdist — are worth singling out: they take a probe specification on the
command line, so they’re a quick way to ask almost any “how often / how slow /
with what arguments” question without writing a program at all.
The full, current list lives upstream at the project’s tools index,
github.com/iovisor/bcc (README.md and the
tools/ directory). Mapped onto the kernel stack, the suite looks like this —
each tool drawn at the layer it observes:
Driving them from Python
Unlike bpftrace and bpftool, BCC tools emit columnar text, not JSON — so the
Python pattern is resolve, run, parse columns. The example’s
examples/66-bcc-tools/bcc_runner.py does exactly that, and it’s several working
examples in one — run --list to see what it knows:
| Wrapper mode | Tools | Summarized as |
|---|---|---|
| parsed → top-N | execsnoop |
execs per command |
opensnoop, statsnoop |
most-opened paths | |
tcpconnect, tcpaccept, tcplife |
busiest remote host:port |
|
killsnoop |
signals per sender | |
syscount |
calls per syscall | |
| captured as-is | biolatency, runqlat, profile, biotop, cachestat, … |
the tool’s own histogram/table |
| run (output captured) | any other, e.g. funccount 'vfs_*', argdist, trace |
raw output |
Behind that table the wrapper does three small things — resolve, run, parse:
- It resolves a tool across the layouts above: an explicit path, then
/usr/share/bcc/tools/, then$PATH, then the-bpfccsuffix — so the same command works on Fedora and Debian. - It runs the tool for a duration, then sends
SIGINTso summarizing tools (the histogram ones) flush their final table. - It parses the tools it knows into a top-N summary:
execsnoop→ execs per command,opensnoop→ most-opened paths,tcpconnect→ busiest destinationhost:port. For tools it doesn’t have a parser for —biolatency,runqlat,profile— it captures and prints their own summary (already a histogram), so every tool is runnable through one wrapper.
def resolve(tool):
for d in ("/usr/share/bcc/tools", "/sbin", "/usr/sbin"):
p = os.path.join(d, tool)
if os.path.exists(p):
return p
return shutil.which(tool) or shutil.which(tool + "-bpfcc")
The value is the same as the previous two chapters: a one-off command becomes a
repeatable check you can point at a service, summarize, and compare against your
Aya program’s own numbers — tcpconnect’s destination tally beside your
tcpconnlat metric, biolatency’s histogram beside your biopattern one.
What a BCC tool is underneath
To demystify the suite, the example includes examples/66-bcc-tools/hello_bcc.py — a complete BCC
program in a dozen lines, using the bcc Python library directly:
from bcc import BPF
b = BPF(text=r'int hello(void *ctx){ bpf_trace_printk("clone\n"); return 0; }')
b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")
b.trace_print()
That BPF(text=...) call is the whole story: BCC takes the C string, compiles it
with Clang right then, loads it, and attaches it — the runtime compilation
that makes classic BCC flexible and dependency-heavy. Set it beside a Chapter 6
Aya program and the contrast is the book in miniature: BCC compiles C at runtime
from Python; Aya compiles Rust ahead of time into one binary you ship. Both ride
the same kernel verifier and maps; they differ in when and in what language
the program is written and built.
When to use which
The field guide’s through-line: BCC for ready-made depth and quick custom
probes (trace, argdist) when you accept its dependencies; bpftrace
(Chapter 64) for fast one-liners in a small DSL; bpftool (Chapter 65) to
inspect what’s loaded; and Aya for the typed, embeddable, single-binary
programs you operate in production. You reach for the first three to explore and
validate, and Aya to ship — which is exactly how this book used them.
Build, deploy, observe
cd examples/66-bcc-tools && ./demo.sh
The demo resolves the tools on the VM, runs execsnoop and tcpconnect through
the summarizer (printing per-command and per-destination tallies), captures a
biolatency histogram, and runs hello_bcc.py to show runtime compilation in
action. These are terminal tools, so there’s no Grafana panel — though the
wrapper’s parsed summaries are exactly what you’d forward as ebpf_* metrics if
you wanted them on a dashboard.
Cross-check
[vm]$ ls /usr/share/bcc/tools | head # the installed suite
[vm]$ sudo /usr/share/bcc/tools/execsnoop # run one directly
[vm]$ sudo /usr/share/bcc/tools/biolatency 5 1 # one 5-second histogram
Running a tool directly and seeing the same rows your wrapper parsed confirms the summarizer is a thin layer over the suite — the same trust boundary as the previous two chapters.
What you learned
- BCC ships ~100 ready-made tracers (the
*snoop/*latencytools named in every cross-check) in two generations: classic (Python + inline C, runtime-compiled — needs Clang + kernel headers, in/usr/share/bcc/tools/) and libbpf-tools (precompiled CO-RE binaries, no runtime deps). - They emit columnar text, so a Python wrapper resolves, runs, and parses
them into summaries —
execsnoop/opensnoop/tcpconnecttallies, captured histograms — turning the suite into repeatable, comparable checks. - A BCC tool is inline C compiled at runtime (
BPF(text=...)); the contrast with Aya’s ahead-of-time Rust binary is the whole book in miniature — same kernel, different when and what language. Use BCC/bpftrace/bpftool to explore and validate, Aya to ship.
That closes the field guide, and the technical body of the book. The final
chapter is a retrospective: the whole arc from a kprobe counting unlink to
operating a fleet, what carried through, and where eBPF and Aya go next.
Verification status: verified — Fedora 44, kernel 7.1.3. Built and run on the lab VM (Fedora 44, kernel 7.1.3-200.fc44): builds, loads, and attaches cleanly and runs without error. Confirmed on this kernel — attach targets and struct offsets can be version-specific.