Skip to content

Commit c77a9e0

Browse files
committed
runtime: In Frames.Next, delay file/line lookup until just before return
That way we will never have to look up the file/line for the frame that's next to be returned when the user stops calling Next. For the benchmark from #32093: name old time/op new time/op delta Helper-4 948ns ± 1% 836ns ± 3% -11.89% (p=0.000 n=9+9) (#32093 was fixed with a more specific, and better, fix, but this fix is much more general.) Change-Id: I89e796f80c9706706d8d8b30eb14be3a8a442846 Reviewed-on: https://go-review.googlesource.com/c/go/+/178077 Run-TryBot: Keith Randall <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 5a90306 commit c77a9e0

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

src/runtime/symtab.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ type Frame struct {
5252
// if not known. If Func is not nil then Entry ==
5353
// Func.Entry().
5454
Entry uintptr
55+
56+
// The runtime's internal view of the function. This field
57+
// is set (funcInfo.valid() returns true) only for Go functions,
58+
// not for C functions.
59+
funcInfo funcInfo
5560
}
5661

5762
// CallersFrames takes a slice of PC values returned by Callers and
@@ -95,7 +100,6 @@ func (ci *Frames) Next() (frame Frame, more bool) {
95100
pc--
96101
}
97102
name := funcname(funcInfo)
98-
file, line := funcline1(funcInfo, pc, false)
99103
if inldata := funcdata(funcInfo, _FUNCDATA_InlTree); inldata != nil {
100104
inltree := (*[1 << 20]inlinedCall)(inldata)
101105
ix := pcdatavalue(funcInfo, _PCDATA_InlTreeIndex, pc, nil)
@@ -111,16 +115,17 @@ func (ci *Frames) Next() (frame Frame, more bool) {
111115
PC: pc,
112116
Func: f,
113117
Function: name,
114-
File: file,
115-
Line: int(line),
116118
Entry: entry,
119+
funcInfo: funcInfo,
120+
// Note: File,Line set below
117121
})
118122
}
119123

120124
// Pop one frame from the frame list. Keep the rest.
121125
// Avoid allocation in the common case, which is 1 or 2 frames.
122126
switch len(ci.frames) {
123127
case 0: // In the rare case when there are no frames at all, we return Frame{}.
128+
return
124129
case 1:
125130
frame = ci.frames[0]
126131
ci.frames = ci.frameStore[:0]
@@ -133,6 +138,13 @@ func (ci *Frames) Next() (frame Frame, more bool) {
133138
ci.frames = ci.frames[1:]
134139
}
135140
more = len(ci.frames) > 0
141+
if frame.funcInfo.valid() {
142+
// Compute file/line just before we need to return it,
143+
// as it can be expensive. This avoids computing file/line
144+
// for the Frame we find but don't return. See issue 32093.
145+
file, line := funcline1(frame.funcInfo, frame.PC, false)
146+
frame.File, frame.Line = file, int(line)
147+
}
136148
return
137149
}
138150

@@ -157,6 +169,8 @@ func expandCgoFrames(pc uintptr) []Frame {
157169
File: gostring(arg.file),
158170
Line: int(arg.lineno),
159171
Entry: arg.entry,
172+
// funcInfo is zero, which implies !funcInfo.valid().
173+
// That ensures that we use the File/Line info given here.
160174
})
161175
if arg.more == 0 {
162176
break

0 commit comments

Comments
 (0)