@@ -10,30 +10,49 @@ import (
10
10
)
11
11
12
12
const (
13
- minGroupSize = 2
13
+ minGroupSize = 2
14
+ maxRecursiveDepth = 56 // Profiles with deeply recursive stack traces are ignored.
14
15
15
16
tokens = 8
16
17
tokenLen = 16
17
- suffixLen = tokens + tokenLen
18
+ suffixLen = tokens + tokenLen // stacktraces shorter than suffixLen are not considered as truncated or missing stacks copied from
18
19
19
20
tokenBytesLen = tokenLen * 8
20
21
suffixBytesLen = suffixLen * 8
21
22
)
22
23
23
- // MayHaveGoHeapTruncatedStacktraces reports whether there are
24
+ // PotentialTruncatedGoStacktraces reports whether there are
24
25
// any chances that the profile may have truncated stack traces.
25
- func MayHaveGoHeapTruncatedStacktraces (p * profilev1.Profile ) bool {
26
- if ! hasGoHeapSampleTypes (p ) {
26
+ func PotentialTruncatedGoStacktraces (p * profilev1.Profile ) bool {
27
+ var minDepth int
28
+ var maxDepth int
29
+
30
+ if hasGoHeapSampleTypes (p ) {
31
+ minDepth = 32
32
+
33
+ // Go heap profiles in Go 1.23+ have a depth limit of 128 frames. Let's not try to fix truncation if we see any longer stacks.
34
+ maxDepth = 33
35
+ } else if hasGoCPUSampleTypes (p ) {
36
+ minDepth = 64
37
+ } else {
27
38
return false
28
39
}
29
- // Some truncated stacks have depth less than the depth limit (32).
30
- const minDepth = 28
40
+
41
+ // Some truncated heap stacks have depth less than the depth limit.
42
+ // https://github.com/golang/go/blob/f7c330eac7777612574d8a1652fd415391f6095e/src/runtime/mprof.go#L446
43
+ minDepth -= 4
44
+
45
+ deepEnough := false
31
46
for _ , s := range p .Sample {
32
47
if len (s .LocationId ) >= minDepth {
33
- return true
48
+ deepEnough = true
49
+ }
50
+ // when it's too deep we no longer perform reassemlbing of truncated stacktraces
51
+ if maxDepth != 0 && len (s .LocationId ) >= maxDepth {
52
+ return false
34
53
}
35
54
}
36
- return false
55
+ return deepEnough
37
56
}
38
57
39
58
func hasGoHeapSampleTypes (p * profilev1.Profile ) bool {
@@ -50,7 +69,18 @@ func hasGoHeapSampleTypes(p *profilev1.Profile) bool {
50
69
return false
51
70
}
52
71
53
- // RepairGoHeapTruncatedStacktraces repairs truncated stack traces
72
+ func hasGoCPUSampleTypes (p * profilev1.Profile ) bool {
73
+ for _ , st := range p .SampleType {
74
+ switch p .StringTable [st .Type ] {
75
+ case
76
+ "cpu" :
77
+ return true
78
+ }
79
+ }
80
+ return false
81
+ }
82
+
83
+ // RepairGoTruncatedStacktraces repairs truncated stack traces
54
84
// in Go heap profiles.
55
85
//
56
86
// Go heap profile has a depth limit of 32 frames, which often
@@ -66,7 +96,7 @@ func hasGoHeapSampleTypes(p *profilev1.Profile) bool {
66
96
// one by at least 16 frames in a row from the top, and has frames
67
97
// above its root, stack S considered truncated, and the missing part
68
98
// is copied from R.
69
- func RepairGoHeapTruncatedStacktraces (p * profilev1.Profile ) {
99
+ func RepairGoTruncatedStacktraces (p * profilev1.Profile ) {
70
100
// Group stack traces by bottom (closest to the root) locations.
71
101
// Typically, there are very few groups (a hundred or two).
72
102
samples , groups := split (p )
@@ -82,7 +112,7 @@ func RepairGoHeapTruncatedStacktraces(p *profilev1.Profile) {
82
112
if i + 1 < len (groups ) {
83
113
n = groups [i + 1 ]
84
114
}
85
- if s := n - g ; s < minGroupSize {
115
+ if s := n - g ; s < ( minGroupSize - 1 ) {
86
116
continue
87
117
}
88
118
// We take suffix of the first sample in the group.
@@ -168,8 +198,7 @@ func RepairGoHeapTruncatedStacktraces(p *profilev1.Profile) {
168
198
}
169
199
c = n
170
200
j ++
171
- if j == tokenLen {
172
- // Profiles with deeply recursive stack traces are ignored.
201
+ if j == maxRecursiveDepth {
173
202
return
174
203
}
175
204
}
0 commit comments