Skip to content

Commit 9ee6ba0

Browse files
committed
runtime: fix line number for faulting instructions
Unlike function calls, when processing instructions that directly fault we must not subtract 1 from the pc before looking up the file/line information. Since the file/line lookup unconditionally subtracts 1, add 1 to the faulting instruction PCs to compensate. Fixes #34123 Change-Id: Ie7361e3d2f84a0d4f48d97e5a9e74f6291ba7a8b Reviewed-on: https://go-review.googlesource.com/c/go/+/196962 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]>
1 parent 9e914f5 commit 9ee6ba0

File tree

4 files changed

+60
-10
lines changed

4 files changed

+60
-10
lines changed

Diff for: src/runtime/pprof/proto.go

-6
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,7 @@ func (b *profileBuilder) build() {
359359
}
360360
}
361361

362-
// Addresses from stack traces point to the next instruction after each call,
363-
// except for the leaf, which points to where the signal occurred.
364-
// appendLocsForStack expects return PCs so increment the leaf address to
365-
// look like a return PC.
366-
e.stk[0] += 1
367362
locs = b.appendLocsForStack(locs[:0], e.stk)
368-
e.stk[0] -= 1 // undo the adjustment on the leaf.
369363

370364
b.pbSample(values, locs, labels)
371365
}

Diff for: src/runtime/pprof/proto_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ func TestConvertCPUProfile(t *testing.T) {
116116

117117
b := []uint64{
118118
3, 0, 500, // hz = 500
119-
5, 0, 10, uint64(addr1), uint64(addr1 + 2), // 10 samples in addr1
120-
5, 0, 40, uint64(addr2), uint64(addr2 + 2), // 40 samples in addr2
121-
5, 0, 10, uint64(addr1), uint64(addr1 + 2), // 10 samples in addr1
119+
5, 0, 10, uint64(addr1 + 1), uint64(addr1 + 2), // 10 samples in addr1
120+
5, 0, 40, uint64(addr2 + 1), uint64(addr2 + 2), // 40 samples in addr2
121+
5, 0, 10, uint64(addr1 + 1), uint64(addr1 + 2), // 10 samples in addr1
122122
}
123123
p, err := translateCPUProfile(b)
124124
if err != nil {

Diff for: src/runtime/traceback.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,20 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
340340
pc := frame.pc
341341
// backup to CALL instruction to read inlining info (same logic as below)
342342
tracepc := pc
343-
if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
343+
// Normally, pc is a return address. In that case, we want to look up
344+
// file/line information using pc-1, because that is the pc of the
345+
// call instruction (more precisely, the last byte of the call instruction).
346+
// Callers expect the pc buffer to contain return addresses and do the
347+
// same -1 themselves, so we keep pc unchanged.
348+
// When the pc is from a signal (e.g. profiler or segv) then we want
349+
// to look up file/line information using pc, and we store pc+1 in the
350+
// pc buffer so callers can unconditionally subtract 1 before looking up.
351+
// See issue 34123.
352+
// The pc can be at function entry when the frame is initialized without
353+
// actually running code, like runtime.mstart.
354+
if (n == 0 && flags&_TraceTrap != 0) || waspanic || pc == f.entry {
355+
pc++
356+
} else {
344357
tracepc--
345358
}
346359

Diff for: test/fixedbugs/issue34123.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// run
2+
3+
// Copyright 2019 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Make sure that the line number is reported correctly
8+
// for faulting instructions.
9+
10+
package main
11+
12+
import (
13+
"fmt"
14+
"runtime"
15+
)
16+
17+
var x byte
18+
var p *byte
19+
20+
//go:noinline
21+
func f() {
22+
q := p
23+
x = 11 // line 23
24+
*q = 12 // line 24
25+
}
26+
func main() {
27+
defer func() {
28+
recover()
29+
var pcs [10]uintptr
30+
n := runtime.Callers(1, pcs[:])
31+
frames := runtime.CallersFrames(pcs[:n])
32+
for {
33+
f, more := frames.Next()
34+
if f.Function == "main.f" && f.Line != 24 {
35+
panic(fmt.Errorf("expected line 24, got line %d", f.Line))
36+
}
37+
if !more {
38+
break
39+
}
40+
}
41+
}()
42+
f()
43+
}

0 commit comments

Comments
 (0)