rgoussu@goussu: ~/library/go/toolchain
~/library/go/toolchain cat pprof.md

pprof — profiling

# Go's built-in profiler — profile kinds, runtime/pprof and net/http/pprof collection, go tool pprof analysis, continuous profiling, and PGO.

Conceptsaved 2026-08-09 #go#tooling#performance#profiling#runtime

Overview

Profiling is built into the Go runtime, not bolted on: any binary can emit CPU, memory, and contention profiles in the pprof protobuf format, and go tool pprof analyses them. Overhead is low enough to sample production continuously, and since Go 1.21 those same production profiles feed back into the compiler as profile-guided optimization. This is the first tool to reach for on "where does the time/memory go" questions; for "where does the latency go", see the execution tracer.

Key points

  • Two collection routes: runtime/pprof writes profiles programmatically (and go test -cpuprofile/-memprofile for benchmarks); importing net/http/pprof registers HTTP endpoints under /debug/pprof/ for on-demand capture from a live service.
  • CPU profile: sampled at ~100 Hz; captures stacks of running goroutines over a duration (/debug/pprof/profile?seconds=30).
  • heap / allocs: heap shows live objects (inuse_space, default) — use for memory growth; allocs shows cumulative allocation since start (alloc_space) — use for GC pressure. Same data, different default sample index.
  • goroutine: stack dump of every goroutine — the tool for leak hunting (watch the count grow, group by creation site).
  • block / mutex: time blocked on channels/selects and mutex contention; both off by default — enable with runtime.SetBlockProfileRate / runtime.SetMutexProfileFraction.
  • go tool pprof: top (hotspots, flat vs cum), list <func> (annotated source), peek, web (SVG call graph); -http=:8080 opens the interactive UI with flame graph, graph, and source views — the mode worth defaulting to.
  • Comparisons: -base old.pb.gz new.pb.gz diffs two profiles — the honest way to judge an optimization or spot a regression between releases.
  • Continuous profiling: sampling overhead is low single-digit percent, so fleets profile always-on (Parca, Pyroscope, Grafana, cloud profilers) — turning "it was slow at 3am" into a profile you can open.
  • PGO (Go 1.21+): drop a CPU profile as default.pgo next to main and go build uses it to guide inlining and devirtualization; typical gains a few percent for free, from real production behaviour.

Details

Reading the numbers

flat is time in the function itself; cum includes callees. A tall flame-graph tower with a wide base in runtime.mallocgc means allocation pressure — switch to the allocs profile and hunt the allocation sites rather than micro-optimizing the CPU view. Symbols survive in stripped release binaries; pprof needs the binary (or its symbol table) only for list/disassembly views.

Serving pprof safely

import _ "net/http/pprof" registers on http.DefaultServeMux — never expose that mux publicly. Serve it on a separate internal listener (or an admin port) instead; profiles leak function names, and the endpoints can be used to degrade the service.

Examples

# Capture 30s of CPU from a live service, open the web UI
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/profile?seconds=30"

# Live-heap growth investigation
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/heap

# Benchmark-driven, then diff against a baseline
go test -bench=. -cpuprofile new.pb.gz ./ring
go tool pprof -base old.pb.gz new.pb.gz

Related

  • The go command & tool catalog — parent catalog of the toolchain.
  • Execution tracer — the complementary view: pprof finds CPU hotspots, the tracer explains latency.
  • go test — benchmarks are the usual local profile source (-cpuprofile, -memprofile).
  • Performance engineering — the discipline these profiles serve: measure, don't guess.
  • JFR & JMC — the JVM counterpart: event-based flight recording where Go splits sampling (pprof) from tracing.
  • Performance & load testing — profiles under synthetic load close the loop before production does.