124 lines
4.7 KiB
Markdown
124 lines
4.7 KiB
Markdown
# pprof Reference
|
|
|
|
## Enable pprof HTTP Server
|
|
|
|
Pprof endpoints MUST be protected with basic auth — NEVER expose them publicly. They leak sensitive runtime information (goroutine stacks, memory contents) and can be abused to DoS your service (CPU profiling is expensive). Pprof SHOULD be toggled via a `PPROF_ENABLED` environment variable.
|
|
|
|
### Quick Setup (Development)
|
|
|
|
```go
|
|
import _ "net/http/pprof"
|
|
|
|
func main() {
|
|
go func() {
|
|
log.Println(http.ListenAndServe("localhost:6060", nil))
|
|
}()
|
|
// ... rest of app
|
|
}
|
|
```
|
|
|
|
### Secure Setup (Production)
|
|
|
|
For production, protect endpoints with basic auth:
|
|
|
|
```go
|
|
import "net/http/pprof"
|
|
|
|
func setupPprof(mux *http.ServeMux) {
|
|
if os.Getenv("PPROF_ENABLED") != "true" {
|
|
return
|
|
}
|
|
|
|
// Protect pprof endpoints with basic auth — never expose unauthenticated
|
|
username := os.Getenv("PPROF_USERNAME")
|
|
password := os.Getenv("PPROF_PASSWORD")
|
|
if username == "" || password == "" {
|
|
panic("PPROF_USERNAME and PPROF_PASSWORD must be set when pprof is enabled")
|
|
}
|
|
auth := basicAuth(username, password)
|
|
|
|
mux.Handle("/debug/pprof/", auth(http.HandlerFunc(pprof.Index)))
|
|
mux.Handle("/debug/pprof/cmdline", auth(http.HandlerFunc(pprof.Cmdline)))
|
|
mux.Handle("/debug/pprof/profile", auth(http.HandlerFunc(pprof.Profile)))
|
|
mux.Handle("/debug/pprof/symbol", auth(http.HandlerFunc(pprof.Symbol)))
|
|
mux.Handle("/debug/pprof/trace", auth(http.HandlerFunc(pprof.Trace)))
|
|
|
|
slog.Info("pprof endpoints enabled (basic auth required)")
|
|
}
|
|
|
|
// basicAuth wraps an http.Handler with HTTP Basic Authentication.
|
|
func basicAuth(username, password string) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
u, p, ok := r.BasicAuth()
|
|
if !ok || u != username || subtle.ConstantTimeCompare([]byte(p), []byte(password)) != 1 {
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="pprof"`)
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
## Profile Types
|
|
|
|
| Profile | Command | What It Shows |
|
|
| --- | --- | --- |
|
|
| **CPU** | `go tool pprof profile` | Where CPU time is spent |
|
|
| **Heap** | `go tool pprof heap` | Memory allocations, live objects |
|
|
| **Goroutine** | `go tool pprof goroutine` | Stack traces of all goroutines |
|
|
| **Block** | `go tool pprof block` | Blocking operations (needs SetBlockProfileRate) |
|
|
| **Mutex** | `go tool pprof mutex` | Lock contention (needs SetMutexProfileFraction) |
|
|
| **Alloc** | `go tool pprof -alloc_space heap` | Cumulative allocations (not current heap) |
|
|
|
|
## Capturing Profiles
|
|
|
|
```bash
|
|
# CPU profiles SHOULD capture at least 30 seconds for meaningful data (30s default).
|
|
# Ensure your HTTP server's request timeout exceeds the capture duration.
|
|
curl http://localhost:6060/debug/pprof/profile?seconds=30 > cpu.prof
|
|
|
|
# Heap snapshot
|
|
curl http://localhost:6060/debug/pprof/heap > heap.prof
|
|
|
|
# Goroutine dump (human-readable)
|
|
curl http://localhost:6060/debug/pprof/goroutine?debug=2 > goroutines.txt
|
|
|
|
# Goroutine profile (for pprof analysis)
|
|
curl http://localhost:6060/debug/pprof/goroutine > goroutine.prof
|
|
|
|
# Go 1.26 experimental goroutine leak profile, only with GOEXPERIMENT=goroutineleakprofile
|
|
curl http://localhost:6060/debug/pprof/goroutineleak?debug=2
|
|
go tool pprof http://localhost:6060/debug/pprof/goroutineleak
|
|
|
|
# Mutex contention
|
|
curl http://localhost:6060/debug/pprof/mutex > mutex.prof
|
|
|
|
# Block profile
|
|
curl http://localhost:6060/debug/pprof/block > block.prof
|
|
```
|
|
|
|
## Analyzing and Interpreting Profiles
|
|
|
|
→ See `samber/cc-skills-golang@golang-benchmark` skill (pprof.md) for interpreting profiles: `top`, `list`, `peek`, common profile patterns (flat vs cum, GC churn, memory leaks), and compiler diagnostics. See also compiler-analysis.md for escape analysis and inlining decisions.
|
|
|
|
**Quick start:**
|
|
|
|
```bash
|
|
go tool pprof cpu.prof # interactive analysis
|
|
go tool pprof -http=:8080 cpu.prof # graphical flamegraph
|
|
go tool pprof -base heap1.prof heap2.prof # compare heap snapshots
|
|
```
|
|
|
|
## Remote Profiling (Production)
|
|
|
|
For production servers, replace `localhost:6060` with your server address and use basic auth credentials.
|
|
|
|
**Safety:** idle pprof endpoints have low overhead, but profile captures are not free. CPU profiling samples for the requested duration, heap profiles may trigger extra work, and block/mutex profiles add runtime overhead when enabled.
|
|
|
|
---
|
|
|
|
→ See `samber/cc-skills-golang@golang-observability` skill for continuous profiling with Pyroscope. → See `samber/cc-skills-golang@golang-benchmark` skill for investigation session setup and Prometheus-based performance tracking.
|