Skip to content

Commit 4abb46f

Browse files
committed
bytesprofile: add Profile.Collect
Add a method to bytesprofile.Profile to return the collected statistics in a structured format. This can be used to return results from a SQL built-in in CockroachDB.
1 parent 14f7727 commit 4abb46f

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

internal/bytesprofile/bytesprofile.go

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package bytesprofile
77
import (
88
"cmp"
99
"fmt"
10+
"iter"
1011
"maps"
1112
"runtime"
1213
"slices"
@@ -56,6 +57,23 @@ func (p *Profile) Record(bytes int64) {
5657
p.samples[stack] = curr
5758
}
5859

60+
func (p *Profile) all() iter.Seq2[stack, aggSamples] {
61+
return func(yield func(stack, aggSamples) bool) {
62+
p.mu.Lock()
63+
defer p.mu.Unlock()
64+
// Sort the stacks by bytes in descending order.
65+
uniqueStacks := slices.SortedFunc(maps.Keys(p.samples), func(a, b stack) int {
66+
return -cmp.Compare(p.samples[a].bytes, p.samples[b].bytes)
67+
})
68+
for _, stack := range uniqueStacks {
69+
samples := p.samples[stack]
70+
if !yield(stack, samples) {
71+
break
72+
}
73+
}
74+
}
75+
}
76+
5977
// TODO(jackson): We could add the ability to export the profile to a pprof file
6078
// (which internally is just a protocol buffer). Ideally the Go standard library
6179
// would provide facilities for this (e.g., golang/go#18454). The runtime/pprof
@@ -64,20 +82,15 @@ func (p *Profile) Record(bytes int64) {
6482

6583
// String returns a string representation of the stacks captured by the profile.
6684
func (p *Profile) String() string {
67-
p.mu.Lock()
68-
defer p.mu.Unlock()
69-
// Sort the stacks by bytes in descending order.
70-
uniqueStacks := slices.SortedFunc(maps.Keys(p.samples), func(a, b stack) int {
71-
return -cmp.Compare(p.samples[a].bytes, p.samples[b].bytes)
72-
})
7385
var sb strings.Builder
74-
for i, stack := range uniqueStacks {
86+
var i int
87+
for stack, stats := range p.all() {
7588
if i > 0 {
7689
sb.WriteString("\n")
7790
}
7891
fmt.Fprintf(&sb, "%d: Count: %d (%s), Bytes: %d (%s)\n", i,
79-
p.samples[stack].count, humanize.Count.Int64(p.samples[stack].count),
80-
p.samples[stack].bytes, humanize.Bytes.Int64(p.samples[stack].bytes))
92+
stats.count, humanize.Count.Int64(stats.count),
93+
stats.bytes, humanize.Bytes.Int64(stats.bytes))
8194
frames := runtime.CallersFrames(stack.trimmed())
8295
for {
8396
frame, more := frames.Next()
@@ -86,6 +99,39 @@ func (p *Profile) String() string {
8699
break
87100
}
88101
}
102+
i++
89103
}
90104
return sb.String()
91105
}
106+
107+
// StackStats contains the stack trace and statistics for a given stack.
108+
type StackStats struct {
109+
Stack string
110+
Bytes int64
111+
Count int64
112+
}
113+
114+
// Collect returns a slice of StackStats, sorted by bytes in descending order.
115+
func (p *Profile) Collect() []StackStats {
116+
var stats []StackStats
117+
var sb strings.Builder
118+
for stack, samples := range p.all() {
119+
sb.Reset()
120+
frames := runtime.CallersFrames(stack.trimmed())
121+
for {
122+
frame, more := frames.Next()
123+
fmt.Fprintf(&sb, " %s\n %s:%d", frame.Function, frame.File, frame.Line)
124+
if !more {
125+
break
126+
}
127+
sb.WriteString("\n")
128+
}
129+
130+
stats = append(stats, StackStats{
131+
Stack: sb.String(),
132+
Bytes: samples.bytes,
133+
Count: samples.count,
134+
})
135+
}
136+
return stats
137+
}

internal/bytesprofile/bytesprofile_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,12 @@ func TestBytesProfile(t *testing.T) {
2121
s := p.String()
2222
require.Contains(t, s, "0: Count: 100 (100), Bytes: 4950 (4.8KB)")
2323
require.Contains(t, s, "1: Count: 10 (10), Bytes: 495 (495B)")
24+
25+
stacks := p.Collect()
26+
require.Len(t, stacks, 2)
27+
require.Equal(t, stacks[0].Bytes, int64(4950))
28+
require.Equal(t, stacks[0].Count, int64(100))
29+
require.Equal(t, stacks[1].Bytes, int64(495))
30+
require.Equal(t, stacks[1].Count, int64(10))
2431
t.Log(s)
2532
}

0 commit comments

Comments
 (0)