Day 155 — pprof CPU & heap profiling¶
Month 6 · Week 3 · ⬅ Day 154 · Day 156 ➡ · Journal index
🎯 Learning Objective¶
Capture CPU and heap profiles from a Go program two ways — programmatically with runtime/pprof and live over HTTP with net/http/pprof — and read them with go tool pprof.
📚 Topics¶
- CPU profiling (sampling) vs heap profiling (snapshot of live allocations)
runtime/pprof(StartCPUProfile/StopCPUProfile,WriteHeapProfile)net/http/pprofblank-import endpoint;go tool pproftop/list/web
📖 Reading / Sources¶
-
runtime/pprofpackage docs -
net/http/pprofpackage docs - Go blog — Profiling Go Programs
- Diagnostics — Profiling
📝 Notes¶
- A CPU profile is statistical: the runtime interrupts ~100×/sec (
runtime.SetCPUProfileRate) and records the running stack. A function only shows up if it actually consumes CPU long enough to be sampled — an idle program yields an empty profile → [[pprof]]. - A heap profile is a snapshot of allocations alive right now, not a time-series. Call
runtime.GC()beforeWriteHeapProfileso the snapshot reflects truly-live memory (drops uncollected garbage). - Only one CPU profile can run at a time;
StartCPUProfileerrors if one is already active. Always pair it with adefer pprof.StopCPUProfile()(or stop explicitly) — the file is only valid after Stop flushes. - The heap profile has four views:
inuse_space/inuse_objects(live, the default) andalloc_space/alloc_objects(cumulative since start). Usealloc_*to hunt allocation churn;inuse_*to hunt leaks. net/http/pprof:import _ "net/http/pprof"registers/debug/pprof/*onhttp.DefaultServeMuxas a side effect. Serve it on an admin-only port (e.g.localhost:6060) — never on the public API; the endpoints leak internals and let anyone trigger expensive dumps → [[pprof-security]].- Read profiles with
go tool pprof <file|url>:top(hottest functions),list Func(annotated source),web(SVG call graph, needs graphviz),peek. Add?seconds=30to the HTTP CPU endpoint to set the sampling window. - Other built-in profiles:
goroutine(dump + counts — find leaks/deadlocks),block,mutex(both need explicit rate-enabling),threadcreate, plus the execution tracer at/debug/pprof/trace(read withgo tool trace). - Profile labels (
pprof.Do/pprof.Labels) tag samples (e.g. by request route) so you can slice a CPU profile by tag.
💻 Code Examples¶
// Programmatic CPU profile: valid only after StopCPUProfile flushes.
f, _ := os.Create("cpu.pprof")
if err := pprof.StartCPUProfile(f); err != nil { log.Fatal(err) }
doWork()
pprof.StopCPUProfile()
f.Close()
// Heap snapshot: GC first so it reflects live memory.
runtime.GC()
h, _ := os.Create("heap.pprof")
pprof.WriteHeapProfile(h)
h.Close()
Full code:
examples/month-06/pprof/main.go· Run:go run ./examples/month-06/pprof
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
| Write & read CPU + heap profiles | ✅ | examples/month-06/pprof |
| Kill allocations after profiling (append pattern) | ✅ | exercises/month-06/week-3/allocfree |
🐛 Mistakes Made¶
- Read the CPU profile file before calling
StopCPUProfile→ it was empty/garbage. Stop flushes; read after. - Forgot
runtime.GC()before the heap snapshot, so dead-but-uncollected objects inflatedinuse_space.
❓ Open Questions¶
- When is the execution tracer (
go tool trace) the right tool over pprof? (Latency/scheduling/blocking questions, not "where's the CPU going".)
🧠 Active Recall (answer without looking)¶
- Q: Why can a CPU-bound function be missing from a CPU profile?
A
CPU profiling samples the stack ~100×/sec. If the function ran but the sampler never landed on it (too fast, or the program ran only briefly), it won't appear. Profiles are statistical, not exhaustive — run longer or under more load to get signal.
2. Q: Difference between the heap profile's inuse_space and alloc_space views? A
inuse_space is memory live at the snapshot instant (good for finding leaks / what's retained). alloc_space is cumulative bytes allocated since the program started, including freed memory (good for finding allocation churn / GC pressure).
🪶 Feynman Reflection¶
A CPU profile is like a time-lapse camera that snaps the call stack a hundred times a second — whoever's on stage most often is your hot path. A heap profile is a single still photo of everything currently holding memory. One answers "where is time spent," the other "what is holding RAM."
🕳️ Knowledge Gaps¶
- Reading flame graphs /
go tool pprof -httpUI fluently — practice on a real profile. - Block & mutex profiles: when to enable their rates and what the output means.
✅ Summary¶
I can capture CPU and heap profiles programmatically and via the HTTP endpoint, and navigate them with top/list, knowing CPU is sampled and heap is a live snapshot.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 156: escape analysis — why those heap allocations happen and how to read
-gcflags=-m.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: docs(journal): pprof cpu & heap profiling (day 155)