diff --git a/bin/collect-profiles.sh b/bin/collect-profiles.sh index eba6495f8..25032a465 100755 --- a/bin/collect-profiles.sh +++ b/bin/collect-profiles.sh @@ -24,6 +24,8 @@ fi echo Collecting goroutine stacks curl -s -o goroutines.stacks "$SOURCE_URL"'/debug/pprof/goroutine?debug=2' +curl -s -o goroutines.stacks.full "$SOURCE_URL"'/debug/stack' + echo Collecting goroutine profile go tool pprof -symbolize=remote -svg -output goroutine.svg "$SOURCE_URL/debug/pprof/goroutine" diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index c22dc8054..858f38997 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -660,6 +660,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error corehttp.VersionOption(), defaultMux("/debug/vars"), defaultMux("/debug/pprof/"), + defaultMux("/debug/stack"), corehttp.MutexFractionOption("/debug/pprof-mutex/"), corehttp.BlockProfileRateOption("/debug/pprof-block/"), corehttp.MetricsScrapingOption("/debug/metrics/prometheus"), diff --git a/cmd/ipfs/debug.go b/cmd/ipfs/debug.go new file mode 100644 index 000000000..d3fa90940 --- /dev/null +++ b/cmd/ipfs/debug.go @@ -0,0 +1,15 @@ +package main + +import ( + "net/http" + + "github.com/ipfs/go-ipfs/core/commands" +) + +func init() { + http.HandleFunc("/debug/stack", + func(w http.ResponseWriter, _ *http.Request) { + _ = commands.WriteAllGoroutineStacks(w) + }, + ) +} diff --git a/core/commands/profile.go b/core/commands/profile.go index 46c3ee19b..f92c3169a 100644 --- a/core/commands/profile.go +++ b/core/commands/profile.go @@ -121,6 +121,25 @@ However, it could reveal: }, } +func WriteAllGoroutineStacks(w io.Writer) error { + // this is based on pprof.writeGoroutineStacks, and removes the 64 MB limit + buf := make([]byte, 1<<20) + for i := 0; ; i++ { + n := runtime.Stack(buf, true) + if n < len(buf) { + buf = buf[:n] + break + } + // if len(buf) >= 64<<20 { + // // Filled 64 MB - stop there. + // break + // } + buf = make([]byte, 2*len(buf)) + } + _, err := w.Write(buf) + return err +} + func writeProfiles(ctx context.Context, cpuProfileTime time.Duration, w io.Writer) error { archive := zip.NewWriter(w) @@ -143,6 +162,17 @@ func writeProfiles(ctx context.Context, cpuProfileTime time.Duration, w io.Write file: "heap.pprof", }} + { + out, err := archive.Create("goroutines-all.stacks") + if err != nil { + return err + } + err = WriteAllGoroutineStacks(out) + if err != nil { + return err + } + } + for _, profile := range profiles { prof := pprof.Lookup(profile.name) out, err := archive.Create(profile.file) diff --git a/test/sharness/t0152-profile.sh b/test/sharness/t0152-profile.sh index 6a45dbf16..13b40517b 100755 --- a/test/sharness/t0152-profile.sh +++ b/test/sharness/t0152-profile.sh @@ -61,4 +61,8 @@ test_expect_success "goroutines stacktrace is valid" ' grep -q "goroutine" "profiles/goroutines.stacks" ' +test_expect_success "full goroutines stacktrace is valid" ' + grep -q "goroutine" "profiles/goroutines-all.stacks" +' + test_done