Files
coder/codersdk/debug.go
Kacper Sawicki df2360f56a feat(coderd): add consolidated /debug/profile endpoint for pprof collection (#22892)
## Summary

Adds a new `GET /api/v2/debug/profile` endpoint that collects multiple
pprof profiles in a single request and returns them as a tar.gz archive.
This allows collecting profiles (including block and mutex) without
requiring `CODER_PPROF_ENABLE` to be set, and without restarting
`coderd`.

Closes #21679

## What it does

The endpoint:
- Temporarily enables block and mutex profiling (normally disabled at
runtime)
- Runs CPU profile and/or trace for a configurable duration (default
10s, max 60s)
- Collects snapshot profiles (heap, allocs, block, mutex, goroutine,
threadcreate)
- Returns a tar.gz archive containing all requested `.prof` files
- Uses an atomic bool to prevent concurrent collections (returns 409
Conflict)
- Is protected by the existing debug endpoint RBAC (owner-only)

**Supported profile types:** cpu, heap, allocs, block, mutex, goroutine,
threadcreate, trace

**Query parameters:**
- `duration`: How long to run timed profiles (default: `10s`, max:
`60s`)
- `profiles`: Comma-separated list of profile types (default:
`cpu,heap,allocs,block,mutex,goroutine`)

## Additional changes

- **SDK client method** (`codersdk.Client.DebugCollectProfile`) for easy
programmatic access
- **`coder support bundle --pprof` integration**: tries the consolidated
endpoint first, falls back to individual `/debug/pprof/*` endpoints for
older servers
- **8 new tests** covering defaults, custom profiles, trace+CPU,
validation errors, authorization, and conflict detection
2026-03-13 14:09:39 +00:00

57 lines
1.6 KiB
Go

package codersdk
import (
"context"
"io"
"net/http"
"net/url"
"strings"
"time"
"golang.org/x/xerrors"
)
// DebugProfileDurationMax is the maximum duration the server will accept
// for a profile collection. Callers should ensure their context deadline
// exceeds this to avoid premature cancellation.
const DebugProfileDurationMax = 60 * time.Second
// DebugProfileOptions are options for collecting debug profiles from the
// server via the consolidated /debug/profile endpoint.
type DebugProfileOptions struct {
// Duration controls how long time-based profiles (cpu, trace) run.
// Zero uses the server default (10s).
Duration time.Duration
// Profiles is the list of profile types to collect. Nil or empty uses
// the server default (cpu, heap, allocs, block, mutex, goroutine).
Profiles []string
}
// DebugCollectProfile fetches a tar.gz archive of pprof profiles from the
// server. The caller is responsible for closing the returned ReadCloser.
func (c *Client) DebugCollectProfile(ctx context.Context, opts DebugProfileOptions) (io.ReadCloser, error) {
qp := url.Values{}
if opts.Duration > 0 {
qp.Set("duration", opts.Duration.String())
}
if len(opts.Profiles) > 0 {
qp.Set("profiles", strings.Join(opts.Profiles, ","))
}
reqPath := "/api/v2/debug/profile"
if len(qp) > 0 {
reqPath += "?" + qp.Encode()
}
resp, err := c.Request(ctx, http.MethodPost, reqPath, nil)
if err != nil {
return nil, xerrors.Errorf("request debug profile: %w", err)
}
if resp.StatusCode != http.StatusOK {
defer resp.Body.Close()
return nil, ReadBodyAsError(resp)
}
return resp.Body, nil
}