Skip to content

Commit ba8310c

Browse files
rhyshcherrymui
authored andcommitted
runtime/pprof: fix allFrames cache
The compiler may choose to inline multiple layers of function call, such that A calling B calling C may end up with all of the instructions for B and C written as part of A's function body. Within that function body, some PCs will represent code from function A. Some will represent code from function B, and for each of those the runtime will have an instruction attributable to A that it can report as its caller. Others will represent code from function C, and for each of those the runtime will have an instruction attributable to B and an instruction attributable to A that it can report as callers. When a profiling signal arrives at an instruction in B (as inlined in A) that the runtime also uses to describe calls to C, the profileBuilder ends up with an incorrect cache of allFrames results. That PC should lead to a location record in the profile that represents the frames B<-A, but the allFrames cache's view should expand the PC only to the B frame. Otherwise, when a profiling signal arrives at an instruction in C (as inlined in B in A), the PC stack C,B,A can get expanded to the frames C,B<-A,A as follows: The inlining deck starts empty. The first tryAdd call proposes PC C and frames C, which the deck accepts. The second tryAdd call proposes PC B and, due to the incorrect caching, frames B,A. (A fresh call to allFrames with PC B would return the frame list B.) The deck accepts that PC and frames. The third tryAdd call proposes PC A and frames A. The deck rejects those because a call from A to A cannot possibly have been inlined. This results in a new location record in the profile representing the frames C<-B<-A (good), as called by A (bad). The bug is the cached expansion of PC B to frames B<-A. That mapping is only appropriate for the resulting protobuf-format profile. The cache needs to reflect the results of a call to allFrames, which expands the PC B to the single frame B. For #50996 For #52693 Fixes #52764 Change-Id: I36d080f3c8a05650cdc13ced262189c33b0083b0 Reviewed-on: https://go-review.googlesource.com/c/go/+/404995 Reviewed-by: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Rhys Hiltner <rhys@justin.tv> Reviewed-by: Cherry Mui <cherryyz@google.com>
1 parent 81d5671 commit ba8310c

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

src/runtime/pprof/proto.go

+22-8
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,10 @@ type locInfo struct {
246246
// https://github.com/golang/go/blob/d6f2f833c93a41ec1c68e49804b8387a06b131c5/src/runtime/traceback.go#L347-L368
247247
pcs []uintptr
248248

249-
// results of allFrames call for this PC
250-
frames []runtime.Frame
251-
symbolizeResult symbolizeFlag
249+
// firstPCFrames and firstPCSymbolizeResult hold the results of the
250+
// allFrames call for the first (leaf-most) PC this locInfo represents
251+
firstPCFrames []runtime.Frame
252+
firstPCSymbolizeResult symbolizeFlag
252253
}
253254

254255
// newProfileBuilder returns a new profileBuilder.
@@ -416,7 +417,7 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo
416417
// stack by trying to add it to the inlining deck before assuming
417418
// that the deck is complete.
418419
if len(b.deck.pcs) > 0 {
419-
if added := b.deck.tryAdd(addr, l.frames, l.symbolizeResult); added {
420+
if added := b.deck.tryAdd(addr, l.firstPCFrames, l.firstPCSymbolizeResult); added {
420421
stk = stk[1:]
421422
continue
422423
}
@@ -520,12 +521,21 @@ type pcDeck struct {
520521
pcs []uintptr
521522
frames []runtime.Frame
522523
symbolizeResult symbolizeFlag
524+
525+
// firstPCFrames indicates the number of frames associated with the first
526+
// (leaf-most) PC in the deck
527+
firstPCFrames int
528+
// firstPCSymbolizeResult holds the results of the allFrames call for the
529+
// first (leaf-most) PC in the deck
530+
firstPCSymbolizeResult symbolizeFlag
523531
}
524532

525533
func (d *pcDeck) reset() {
526534
d.pcs = d.pcs[:0]
527535
d.frames = d.frames[:0]
528536
d.symbolizeResult = 0
537+
d.firstPCFrames = 0
538+
d.firstPCSymbolizeResult = 0
529539
}
530540

531541
// tryAdd tries to add the pc and Frames expanded from it (most likely one,
@@ -554,6 +564,10 @@ func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symb
554564
d.pcs = append(d.pcs, pc)
555565
d.frames = append(d.frames, frames...)
556566
d.symbolizeResult |= symbolizeResult
567+
if len(d.pcs) == 1 {
568+
d.firstPCFrames = len(d.frames)
569+
d.firstPCSymbolizeResult = symbolizeResult
570+
}
557571
return true
558572
}
559573

@@ -581,10 +595,10 @@ func (b *profileBuilder) emitLocation() uint64 {
581595

582596
id := uint64(len(b.locs)) + 1
583597
b.locs[addr] = locInfo{
584-
id: id,
585-
pcs: append([]uintptr{}, b.deck.pcs...),
586-
symbolizeResult: b.deck.symbolizeResult,
587-
frames: append([]runtime.Frame{}, b.deck.frames...),
598+
id: id,
599+
pcs: append([]uintptr{}, b.deck.pcs...),
600+
firstPCSymbolizeResult: b.deck.firstPCSymbolizeResult,
601+
firstPCFrames: append([]runtime.Frame{}, b.deck.frames[:b.deck.firstPCFrames]...),
588602
}
589603

590604
start := b.pb.startMessage()

0 commit comments

Comments
 (0)