From ed57d7bb15992bf7ffbbe643401b03f0a418663c Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 23 Sep 2021 13:11:04 -0700 Subject: [PATCH] debug/gosym: refactor handling of funcdata MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit We do a bunch of manual offset calculations everywhere. Add a bit of type safety and some helpers. In addition to making the code clearer and providing a place to hang some documentation, it also makes upcoming changes easier. name old time/op new time/op delta 115/NewLineTable-8 79.9ns ± 1% 90.2ns ±23% ~ (p=0.234 n=9+10) 115/NewTable-8 72.0µs ± 1% 73.4µs ± 1% +1.96% (p=0.000 n=8+8) 115/LineToPC-8 53.3µs ± 1% 54.4µs ± 1% +2.02% (p=0.000 n=10+10) 115/PCToLine-8 249ns ± 0% 249ns ± 2% ~ (p=0.147 n=9+10) name old alloc/op new alloc/op delta 115/NewLineTable-8 384B ± 0% 384B ± 0% ~ (all equal) 115/NewTable-8 164kB ± 0% 164kB ± 0% ~ (p=0.610 n=10+10) 115/LineToPC-8 0.00B 0.00B ~ (all equal) 115/PCToLine-8 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta 115/NewLineTable-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) 115/NewTable-8 1.04k ± 0% 1.04k ± 0% ~ (all equal) 115/LineToPC-8 0.00 0.00 ~ (all equal) 115/PCToLine-8 0.00 0.00 ~ (all equal) Change-Id: If357dce5ae4277e6ddc6d90ba6b5b83e470b9121 Reviewed-on: https://go-review.googlesource.com/c/go/+/352951 Trust: Josh Bleecher Snyder Trust: Brad Fitzpatrick Run-TryBot: Josh Bleecher Snyder Reviewed-by: Cherry Mui Reviewed-by: Brad Fitzpatrick TryBot-Result: Go Bot --- src/debug/gosym/pclntab.go | 83 ++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go index 5d18410a78..bf97976b3c 100644 --- a/src/debug/gosym/pclntab.go +++ b/src/debug/gosym/pclntab.go @@ -279,13 +279,13 @@ func (t *LineTable) go12Funcs() []Func { f := &funcs[i] f.Entry = t.uintptr(t.functab[2*i*int(t.ptrsize):]) f.End = t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):]) - info := t.funcdata[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] + info := t.funcData(uint32(i)) f.LineTable = t - f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) + f.FrameSize = int(info.deferreturn()) f.Sym = &Sym{ Value: f.Entry, Type: 'T', - Name: t.funcName(t.binary.Uint32(info[t.ptrsize:])), + Name: t.funcName(info.nameoff()), GoType: 0, Func: f, } @@ -293,10 +293,10 @@ func (t *LineTable) go12Funcs() []Func { return funcs } -// findFunc returns the func corresponding to the given program counter. -func (t *LineTable) findFunc(pc uint64) []byte { +// findFunc returns the funcData corresponding to the given program counter. +func (t *LineTable) findFunc(pc uint64) funcData { if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) { - return nil + return funcData{} } // The function table is a list of 2*nfunctab+1 uintptrs, @@ -307,7 +307,8 @@ func (t *LineTable) findFunc(pc uint64) []byte { m := nf / 2 fm := f[2*t.ptrsize*m:] if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) { - return t.funcdata[t.uintptr(fm[t.ptrsize:]):] + data := t.funcdata[t.uintptr(fm[t.ptrsize:]):] + return funcData{t: t, data: data} } else if pc < t.uintptr(fm) { nf = m } else { @@ -315,7 +316,7 @@ func (t *LineTable) findFunc(pc uint64) []byte { nf -= m + 1 } } - return nil + return funcData{} } // readvarint reads, removes, and returns a varint from *pp. @@ -361,6 +362,47 @@ func (t *LineTable) string(off uint32) string { return t.stringFrom(t.funcdata, off) } +// funcData is memory corresponding to an _func struct. +type funcData struct { + t *LineTable // LineTable this data is a part of + data []byte // raw memory for the function +} + +// funcData returns the ith funcData in t.functab. +func (t *LineTable) funcData(i uint32) funcData { + data := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] + return funcData{t: t, data: data} +} + +// IsZero reports whether f is the zero value. +func (f funcData) IsZero() bool { + return f.t == nil && f.data == nil +} + +// entryPC returns the func's entry PC. +func (f funcData) entryPC() uint64 { + return f.t.uintptr(f.data) +} + +func (f funcData) nameoff() uint32 { return f.field(1) } +func (f funcData) deferreturn() uint32 { return f.field(3) } +func (f funcData) pcfile() uint32 { return f.field(5) } +func (f funcData) pcln() uint32 { return f.field(6) } +func (f funcData) cuOffset() uint32 { return f.field(8) } + +// field returns the nth field of the _func struct. +// It panics if n == 0 or n > 9; for n == 0, call f.entryPC. +// Most callers should use a named field accessor (just above). +func (f funcData) field(n uint32) uint32 { + if n == 0 || n > 9 { + panic("bad funcdata field") + } + sz0 := f.t.ptrsize + off := sz0 + (n-1)*4 // subsequent fields are 4 bytes each + data := f.data[off:] + return f.t.binary.Uint32(data) +} + // step advances to the next pc, value pair in the encoded table. func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { uvdelta := t.readvarint(p) @@ -451,11 +493,11 @@ func (t *LineTable) go12PCToLine(pc uint64) (line int) { }() f := t.findFunc(pc) - if f == nil { + if f.IsZero() { return -1 } - entry := t.uintptr(f) - linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + entry := f.entryPC() + linetab := f.pcln() return int(t.pcvalue(linetab, entry, pc)) } @@ -468,11 +510,11 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) { }() f := t.findFunc(pc) - if f == nil { + if f.IsZero() { return "" } - entry := t.uintptr(f) - filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + entry := f.entryPC() + filetab := f.pcfile() fno := t.pcvalue(filetab, entry, pc) if t.version == ver12 { if fno <= 0 { @@ -484,7 +526,7 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) { if fno < 0 { // 0 is valid for ≥ 1.16 return "" } - cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) + cuoff := f.cuOffset() if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) { return t.stringFrom(t.filetab, fnoff) } @@ -510,13 +552,12 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { // mapping file number to a list of functions with code from that file. var cutab []byte for i := uint32(0); i < t.nfunctab; i++ { - f := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] - entry := t.uintptr(f) - filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) - linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + f := t.funcData(i) + entry := f.entryPC() + filetab := f.pcfile() + linetab := f.pcln() if t.version == ver116 { - cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) * 4 - cutab = t.cutab[cuoff:] + cutab = t.cutab[f.cuOffset()*4:] } pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab) if pc != 0 { -- 2.48.1