mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user