Skip to content

Commit

Permalink
feat: add GoID to the output of runtime.GoroutineProfile()
Browse files Browse the repository at this point in the history
  • Loading branch information
vaind committed May 29, 2024
1 parent ca5fc8d commit f790446
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 3 deletions.
8 changes: 7 additions & 1 deletion src/runtime/mprof.go
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,7 @@ func mutexevent(cycles int64, skip int) {
// A StackRecord describes a single execution stack.
type StackRecord struct {
Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
goid uint64 // Goroutine ID for the first record (or 0 if undefined by the struct creator).
}

// Stack returns the stack trace associated with the record,
Expand All @@ -843,6 +844,11 @@ func (r *StackRecord) Stack() []uintptr {
return r.Stack0[0:]
}

// GoID returns an identifier for the Goroutine this stack was captured for, or zero if undefined.
func (r *StackRecord) GoID() uint64 {
return r.goid
}

// MemProfileRate controls the fraction of memory allocations
// that are recorded and reported in the memory profile.
// The profiler aims to sample an average of
Expand Down Expand Up @@ -1438,7 +1444,6 @@ func goroutineProfileWithLabelsSync(p []StackRecord, labels []unsafe.Pointer) (n
// Most clients should use the [runtime/pprof] package instead
// of calling GoroutineProfile directly.
func GoroutineProfile(p []StackRecord) (n int, ok bool) {

return goroutineProfileWithLabels(p, nil)
}

Expand All @@ -1449,6 +1454,7 @@ func saveg(pc, sp uintptr, gp *g, r *StackRecord) {
if n < len(r.Stack0) {
r.Stack0[n] = 0
}
r.goid = gp.goid
}

// Stack formats a stack trace of the calling goroutine into buf
Expand Down
17 changes: 15 additions & 2 deletions src/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,17 +344,19 @@ func TestAppendSliceGrowth(t *testing.T) {
}
}

func TestGoroutineProfileTrivial(t *testing.T) {
func TestGoroutineProfile(t *testing.T) {
// Calling GoroutineProfile twice in a row should find the same number of goroutines,
// but it's possible there are goroutines just about to exit, so we might end up
// with fewer in the second call. Try a few times; it should converge once those
// zombies are gone.
var records []StackRecord
for i := 0; ; i++ {
n1, ok := GoroutineProfile(nil) // should fail, there's at least 1 goroutine
if n1 < 1 || ok {
t.Fatalf("GoroutineProfile(nil) = %d, %v, want >0, false", n1, ok)
}
n2, ok := GoroutineProfile(make([]StackRecord, n1))
records = make([]StackRecord, n1)
n2, ok := GoroutineProfile(records)
if n2 == n1 && ok {
break
}
Expand All @@ -363,6 +365,17 @@ func TestGoroutineProfileTrivial(t *testing.T) {
t.Fatalf("GoroutineProfile not converging")
}
}
if len(records) < 1 {
t.Fatalf("GoroutineProfile hasn't collected any records")
}
for _, record := range records {
if len(record.Stack()) < 1 {
t.Fatalf("GoroutineProfile record is missing a stack trace")
}
if record.GoID() < 1 {
t.Fatalf("GoroutineProfile record is missing a GoID")
}
}
}

func BenchmarkGoroutineProfile(b *testing.B) {
Expand Down

0 comments on commit f790446

Please sign in to comment.