diff options
| author | Michael Pratt <mpratt@google.com> | 2022-09-07 13:23:19 -0400 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2022-10-14 14:47:12 +0000 |
| commit | f2656f20ea420ada5f15ef06ddf18d2797e18841 (patch) | |
| tree | ca71ed64eec8c6027dc5f88af83c4509076adb05 /src/runtime/symtab.go | |
| parent | a4b4717f23334547f40f90f1457f3dc086259fa3 (diff) | |
| download | go-f2656f20ea420ada5f15ef06ddf18d2797e18841.tar.xz | |
cmd/compile,cmd/link,runtime: add start line numbers to func metadata
This adds the function "start line number" to runtime._func and
runtime.inlinedCall objects. The "start line number" is the line number
of the func keyword or TEXT directive for assembly.
Subtracting the start line number from PC line number provides the
relative line offset of a PC from the the start of the function. This
helps with source stability by allowing code above the function to move
without invalidating samples within the function.
Encoding start line rather than relative lines directly is convenient
because the pprof format already contains a start line field.
This CL uses a straightforward encoding of explictly including a start
line field in every _func and inlinedCall. It is possible that we could
compress this further in the future. e.g., functions with a prologue
usually have <line of PC 0> == <start line>. In runtime.test, 95% of
functions have <line of PC 0> == <start line>.
According to bent, this is geomean +0.83% binary size vs master and
-0.31% binary size vs 1.19.
Note that //line directives can change the file and line numbers
arbitrarily. The encoded start line is as adjusted by //line directives.
Since this can change in the middle of a function, `line - start line`
offset calculations may not be meaningful if //line directives are in
use.
For #55022.
Change-Id: Iaabbc6dd4f85ffdda294266ef982ae838cc692f6
Reviewed-on: https://go-review.googlesource.com/c/go/+/429638
Run-TryBot: Michael Pratt <mpratt@google.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/runtime/symtab.go')
| -rw-r--r-- | src/runtime/symtab.go | 61 |
1 files changed, 44 insertions, 17 deletions
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 2da9a59b7e..920ec12d54 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -49,6 +49,15 @@ type Frame struct { File string Line int + // startLine is the line number of the beginning of the function in + // this frame. Specifically, it is the line number of the func keyword + // for Go functions. Note that //line directives can change the + // filename and/or line number arbitrarily within a function, meaning + // that the Line - startLine offset is not always meaningful. + // + // This may be zero if not known. + startLine int + // Entry point program counter for the function; may be zero // if not known. If Func is not nil then Entry == // Func.Entry(). @@ -108,6 +117,7 @@ func (ci *Frames) Next() (frame Frame, more bool) { pc-- } name := funcname(funcInfo) + startLine := f.startLine() if inldata := funcdata(funcInfo, _FUNCDATA_InlTree); inldata != nil { inltree := (*[1 << 20]inlinedCall)(inldata) // Non-strict as cgoTraceback may have added bogus PCs @@ -116,16 +126,19 @@ func (ci *Frames) Next() (frame Frame, more bool) { if ix >= 0 { // Note: entry is not modified. It always refers to a real frame, not an inlined one. f = nil - name = funcnameFromNameOff(funcInfo, inltree[ix].nameOff) + ic := inltree[ix] + name = funcnameFromNameOff(funcInfo, ic.nameOff) + startLine = ic.startLine // File/line from funcline1 below are already correct. } } ci.frames = append(ci.frames, Frame{ - PC: pc, - Func: f, - Function: name, - Entry: entry, - funcInfo: funcInfo, + PC: pc, + Func: f, + Function: name, + Entry: entry, + startLine: int(startLine), + funcInfo: funcInfo, // Note: File,Line set below }) } @@ -727,14 +740,16 @@ func FuncForPC(pc uintptr) *Func { // The runtime currently doesn't have function end info, alas. if ix := pcdatavalue1(f, _PCDATA_InlTreeIndex, pc, nil, false); ix >= 0 { inltree := (*[1 << 20]inlinedCall)(inldata) - name := funcnameFromNameOff(f, inltree[ix].nameOff) + ic := inltree[ix] + name := funcnameFromNameOff(f, ic.nameOff) file, line := funcline(f, pc) fi := &funcinl{ - ones: ^uint32(0), - entry: f.entry(), // entry of the real (the outermost) function. - name: name, - file: file, - line: int(line), + ones: ^uint32(0), + entry: f.entry(), // entry of the real (the outermost) function. + name: name, + file: file, + line: line, + startLine: ic.startLine, } return (*Func)(unsafe.Pointer(fi)) } @@ -773,7 +788,7 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) { fn := f.raw() if fn.isInlined() { // inlined version fi := (*funcinl)(unsafe.Pointer(fn)) - return fi.file, fi.line + return fi.file, int(fi.line) } // Pass strict=false here, because anyone can call this function, // and they might just be wrong about targetpc belonging to f. @@ -781,6 +796,17 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) { return file, int(line32) } +// startLine returns the starting line number of the function. i.e., the line +// number of the func keyword. +func (f *Func) startLine() int32 { + fn := f.raw() + if fn.isInlined() { // inlined version + fi := (*funcinl)(unsafe.Pointer(fn)) + return fi.startLine + } + return fn.funcInfo().startLine +} + // findmoduledatap looks up the moduledata for a PC. // // It is nosplit because it's part of the isgoexception @@ -1173,8 +1199,9 @@ func stackmapdata(stkmap *stackmap, n int32) bitvector { // inlinedCall is the encoding of entries in the FUNCDATA_InlTree table. type inlinedCall struct { - funcID funcID // type of the called function - _ [3]byte - nameOff int32 // offset into pclntab for name of called function - parentPc int32 // position of an instruction whose source position is the call site (offset from entry) + funcID funcID // type of the called function + _ [3]byte + nameOff int32 // offset into pclntab for name of called function + parentPc int32 // position of an instruction whose source position is the call site (offset from entry) + startLine int32 // line number of start of function (func keyword/TEXT directive) } |
