mirror of
https://github.com/golang/go
synced 2024-11-11 19:31:37 -07:00
runtime: make FuncForPC return the innermost inlined frame
Returning the innermost frame instead of the outermost makes code that walks the results of runtime.Caller{,s} still work correctly in the presence of mid-stack inlining. Fixes #29582 Change-Id: I2392e3dd5636eb8c6f58620a61cef2194fe660a7 Reviewed-on: https://go-review.googlesource.com/c/156364 Run-TryBot: Keith Randall <khr@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
033b650181
commit
956879dd0b
@ -156,7 +156,7 @@ func racecallback(cmd uintptr, ctx unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceSymbolizeCode(ctx *symbolizeCodeContext) {
|
||||
f := FuncForPC(ctx.pc)
|
||||
f := findfunc(ctx.pc)._Func()
|
||||
if f != nil {
|
||||
file, line := f.FileLine(ctx.pc)
|
||||
if line != 0 {
|
||||
|
@ -663,6 +663,17 @@ type _func struct {
|
||||
nfuncdata uint8 // must be last
|
||||
}
|
||||
|
||||
// Pseudo-Func that is returned for PCs that occur in inlined code.
|
||||
// A *Func can be either a *_func or a *funcinl, and they are distinguished
|
||||
// by the first uintptr.
|
||||
type funcinl struct {
|
||||
zero uintptr // set to 0 to distinguish from _func
|
||||
entry uintptr // entry of the real (the "outermost") frame.
|
||||
name string
|
||||
file string
|
||||
line int
|
||||
}
|
||||
|
||||
// layout of Itab known to compilers
|
||||
// allocated in non-garbage-collected memory
|
||||
// Needs to be in sync with
|
||||
|
@ -466,9 +466,28 @@ func moduledataverify1(datap *moduledata) {
|
||||
// given program counter address, or else nil.
|
||||
//
|
||||
// If pc represents multiple functions because of inlining, it returns
|
||||
// the *Func describing the outermost function.
|
||||
// the a *Func describing the innermost function, but with an entry
|
||||
// of the outermost function.
|
||||
func FuncForPC(pc uintptr) *Func {
|
||||
return findfunc(pc)._Func()
|
||||
f := findfunc(pc)
|
||||
if !f.valid() {
|
||||
return nil
|
||||
}
|
||||
if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil {
|
||||
if ix := pcdatavalue(f, _PCDATA_InlTreeIndex, pc, nil); ix >= 0 {
|
||||
inltree := (*[1 << 20]inlinedCall)(inldata)
|
||||
name := funcnameFromNameoff(f, inltree[ix].func_)
|
||||
file, line := funcline(f, pc)
|
||||
fi := &funcinl{
|
||||
entry: f.entry, // entry of the real (the outermost) function.
|
||||
name: name,
|
||||
file: file,
|
||||
line: int(line),
|
||||
}
|
||||
return (*Func)(unsafe.Pointer(fi))
|
||||
}
|
||||
}
|
||||
return f._Func()
|
||||
}
|
||||
|
||||
// Name returns the name of the function.
|
||||
@ -476,12 +495,22 @@ func (f *Func) Name() string {
|
||||
if f == nil {
|
||||
return ""
|
||||
}
|
||||
fn := f.raw()
|
||||
if fn.entry == 0 { // inlined version
|
||||
fi := (*funcinl)(unsafe.Pointer(fn))
|
||||
return fi.name
|
||||
}
|
||||
return funcname(f.funcInfo())
|
||||
}
|
||||
|
||||
// Entry returns the entry address of the function.
|
||||
func (f *Func) Entry() uintptr {
|
||||
return f.raw().entry
|
||||
fn := f.raw()
|
||||
if fn.entry == 0 { // inlined version
|
||||
fi := (*funcinl)(unsafe.Pointer(fn))
|
||||
return fi.entry
|
||||
}
|
||||
return fn.entry
|
||||
}
|
||||
|
||||
// FileLine returns the file name and line number of the
|
||||
@ -489,6 +518,11 @@ func (f *Func) Entry() uintptr {
|
||||
// The result will not be accurate if pc is not a program
|
||||
// counter within f.
|
||||
func (f *Func) FileLine(pc uintptr) (file string, line int) {
|
||||
fn := f.raw()
|
||||
if fn.entry == 0 { // inlined version
|
||||
fi := (*funcinl)(unsafe.Pointer(fn))
|
||||
return fi.file, fi.line
|
||||
}
|
||||
// Pass strict=false here, because anyone can call this function,
|
||||
// and they might just be wrong about targetpc belonging to f.
|
||||
file, line32 := funcline1(f.funcInfo(), pc, false)
|
||||
|
@ -54,9 +54,9 @@ type wantFrame struct {
|
||||
|
||||
// -1 means don't care
|
||||
var expected = []wantFrame{
|
||||
0: {"main.testCaller", 36},
|
||||
1: {"main.testCaller", 31},
|
||||
2: {"main.testCaller", 27},
|
||||
0: {"main.h", 36},
|
||||
1: {"main.g", 31},
|
||||
2: {"main.f", 27},
|
||||
3: {"main.testCaller", 42},
|
||||
4: {"main.main", 68},
|
||||
5: {"runtime.main", -1},
|
||||
|
@ -31,7 +31,7 @@ func testCallers(skp int) (frames []string) {
|
||||
skip = skp
|
||||
f()
|
||||
for i := 0; i < npcs; i++ {
|
||||
fn := runtime.FuncForPC(pcs[i])
|
||||
fn := runtime.FuncForPC(pcs[i] - 1)
|
||||
frames = append(frames, fn.Name())
|
||||
if fn.Name() == "main.main" {
|
||||
break
|
||||
@ -56,10 +56,10 @@ func testCallersFrames(skp int) (frames []string) {
|
||||
}
|
||||
|
||||
var expectedFrames [][]string = [][]string{
|
||||
0: {"runtime.Callers", "main.testCallers", "main.testCallers", "main.testCallers", "main.testCallers", "main.main"},
|
||||
1: {"main.testCallers", "main.testCallers", "main.testCallers", "main.testCallers", "main.main"},
|
||||
2: {"main.testCallers", "main.testCallers", "main.testCallers", "main.main"},
|
||||
3: {"main.testCallers", "main.testCallers", "main.main"},
|
||||
0: {"runtime.Callers", "main.h", "main.g", "main.f", "main.testCallers", "main.main"},
|
||||
1: {"main.h", "main.g", "main.f", "main.testCallers", "main.main"},
|
||||
2: {"main.g", "main.f", "main.testCallers", "main.main"},
|
||||
3: {"main.f", "main.testCallers", "main.main"},
|
||||
4: {"main.testCallers", "main.main"},
|
||||
5: {"main.main"},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user