]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: make FuncForPC return the innermost inlined frame
authorKeith Randall <keithr@alum.mit.edu>
Sat, 5 Jan 2019 22:31:23 +0000 (14:31 -0800)
committerKeith Randall <khr@golang.org>
Tue, 8 Jan 2019 21:54:04 +0000 (21:54 +0000)
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>
src/runtime/race.go
src/runtime/runtime2.go
src/runtime/symtab.go
test/inline_caller.go
test/inline_callers.go

index 08d53a10d2ba53471ce2daf43b69931b4cb33840..adb2198c55470b82649a76a929d365335506f71a 100644 (file)
@@ -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 {
index 290a7bd3111a7160fb74f072d666f4c63a4ac2f5..df9cbaef203e770510a8d1e0d829219f7b7c7504 100644 (file)
@@ -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
index 245a7e6b01a2e96607f2d52578e502ef2007e6e3..e7ce3de497473ec37247603933254b09e1f3f1c1 100644 (file)
@@ -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)
index 79039a6bb532ebe16f7052b2ddbedc5d5e8bed54..daff145a9229fec989afeb146ca1bbf69d37df9b 100644 (file)
@@ -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},
index f2c05622dd1d71035b3946334e611a30ab5e1de3..ee7d6470728cb916c34f3c4e2f73cfa9a1edb31c 100644 (file)
@@ -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"},
 }