Skip to content

feat: add goroutine-leak collector to 'ipfs diag profile' - #11406

Open
ecgang wants to merge 1 commit into
ipfs:masterfrom
ecgang:feat/goroutineleak
Open

feat: add goroutine-leak collector to 'ipfs diag profile'#11406
ecgang wants to merge 1 commit into
ipfs:masterfrom
ecgang:feat/goroutineleak

Conversation

@ecgang

@ecgang ecgang commented Jul 24, 2026

Copy link
Copy Markdown

Adds an opt-in goroutine-leak collector to ipfs diag profile, backed by Go 1.26's experimental goroutine leak profiler (GOEXPERIMENT=goroutineleakprofile).

What

  • New goroutine-leak collector in profile/profile.go that writes goroutineleak.pprof (proto format, like the other pprof-backed collectors).
  • Requesting it on a build without the experiment fails up front with goroutineleak profile is not available in this build (requires GOEXPERIMENT=goroutineleakprofile). Availability is validated synchronously, after name validation and before any collector starts, so a mixed request cannot leave a partially written archive, and the unknown collector error deterministically wins over the availability error across build variants.
  • /debug/pprof/goroutineleak needs no route changes: the daemon mounts net/http/pprof's handlers, which serve any registered runtime profile by name. Documented in docs/debug-guide.md.
  • The privacy notice in the command help mentions the leaked-goroutine stack traces; changelog entry added under v0.44 Highlights.

Design decisions (flagging for review)

  • Opt-in, not part of the default collector set. Collecting this profile triggers a goroutine-leak-detection GC cycle. Putting it in the default set would silently add that cost to every default ipfs diag profile run the moment kubo builds move to a toolchain with the experiment enabled by default (expected in Go 1.27). That seemed like a decision a maintainer should make deliberately, so the collector is explicit-request only for now; promoting it to the default set later is a two-line change. Happy to flip it in this PR if you would rather have it on by default.
  • Error, not silent skip, when unavailable. Sampling collectors (cpu/mutex/block) skip silently when their options disable them, but a collector the caller explicitly named that cannot run in this build should fail loudly rather than return a successful archive missing the one thing that was asked for.
  • Collector name goroutine-leak follows the issue text; renaming to goroutines-leak for symmetry with the existing goroutines-* collectors is a one-liner if preferred.

Tests

  • TestProfiler covers the gating in the standard suite: goroutine-leak joins the collector list and the expected archive contents only when the profile is available, so the same assertions run under both build modes (the exact-file-count check proves absence on standard builds).
  • TestGoroutineLeakProfile uses a mixed request (version + goroutine-leak): on standard builds it asserts the up-front error and that zero archive entries were written; on experiment builds it asserts both files are present and goroutineleak.pprof is valid gzip-compressed proto.
  • TestUnknownCollectorBeatsAvailability pins the validation order across build variants.
  • Verified locally on macOS/arm64 (Go 1.26.5): go test ./profile/... green with and without GOEXPERIMENT=goroutineleakprofile; test/sharness/t0152-profile.sh passes all 26; TestCommandDocsWidth green; exercised end-to-end against a live daemon in both build modes (default run unchanged; explicit request collects on experiment builds and errors on standard builds; /debug/pprof/goroutineleak returns 200 with gzip proto on experiment builds).

Notes

  • CI never sets GOEXPERIMENT, so the experiment-enabled halves of these tests do not run in CI today. If you want that coverage, I can add a small experiment-enabled go test ./profile/... job in a follow-up; I did not touch .github/ here per AGENTS.md.
  • Pre-existing and unchanged: on any synchronous validation error (including unknown collector on master today), the CLI leaves an empty zip skeleton at the -o path because the output file is created before collection starts. Can address separately if worth fixing.

References

🤖 Generated with Claude Code

Collect Go's goroutineleak profile into goroutineleak.pprof when the
binary was built with GOEXPERIMENT=goroutineleakprofile (a Go 1.26
experiment, expected to be enabled by default in Go 1.27).

The collector is opt-in (not part of the default set) because
collecting the profile triggers a goroutine-leak-detection GC cycle.
Requesting it on a build without the experiment returns a clear error;
availability is validated before any collector starts, so a mixed
request fails up front rather than leaving a partially written
archive. The profile is also served at /debug/pprof/goroutineleak via
the existing pprof mux with no route changes.

Closes ipfs#11191

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ecgang
ecgang requested a review from a team as a code owner July 24, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add goroutine leak profile to ipfs diag profile

1 participant