aboutsummaryrefslogtreecommitdiff
path: root/src/debug
diff options
context:
space:
mode:
authorJeremy Faller <jeremy@golang.org>2020-06-24 14:30:16 -0400
committerJeremy Faller <jeremy@golang.org>2020-07-30 19:36:06 +0000
commitba9c639470cb962a799116fea55c91638752fe87 (patch)
tree2fecdbd49216a01f2ed5d070c5755bf3a6fa80ec /src/debug
parentb3e3c339ff02ccd1cf76eb07513e4f9051a55333 (diff)
downloadgo-ba9c639470cb962a799116fea55c91638752fe87.tar.gz
go-ba9c639470cb962a799116fea55c91638752fe87.zip
[dev.link] cmd/link: add runtime.pcheader
As of July 2020, a fair amount of the new linker's live memory, and runtime is spent generating pclntab. In an effort to streamline that code, this change starts breaking up the generation of runtime.pclntab into smaller chunks that can run later in a link. These changes are described in an (as yet not widely distributed) document that lays out an improved format. Largely the work consists of breaking up runtime.pclntab into smaller pieces, stopping much of the data rewriting, and getting runtime.pclntab into a form where we can reason about its size and look to shrink it. This change is the first part of that work -- just pulling out the header, and demonstrating where a majority of that work will be. Change-Id: I65618d0d0c780f7e5977c9df4abdbd1696fedfcb Reviewed-on: https://go-review.googlesource.com/c/go/+/241598 Run-TryBot: Jeremy Faller <jeremy@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/debug')
-rw-r--r--src/debug/gosym/pclntab.go112
-rw-r--r--src/debug/gosym/pclntab_test.go50
-rw-r--r--src/debug/gosym/testdata/pcln115.gzbin0 -> 124875 bytes
3 files changed, 129 insertions, 33 deletions
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index 7e54a94351..8c7ace17cd 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -14,6 +14,16 @@ import (
"sync"
)
+// version of the pclntab
+type version int
+
+const (
+ verUnknown version = iota
+ ver11
+ ver12
+ ver116
+)
+
// A LineTable is a data structure mapping program counters to line numbers.
//
// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable,
@@ -32,12 +42,17 @@ type LineTable struct {
PC uint64
Line int
- // Go 1.2 state
- mu sync.Mutex
- go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
+ // This mutex is used to keep parsing of pclntab synchronous.
+ mu sync.Mutex
+
+ // Contains the version of the pclntab section.
+ version version
+
+ // Go 1.2/1.16 state
binary binary.ByteOrder
quantum uint32
ptrsize uint32
+ funcdata []byte
functab []byte
nfunctab uint32
filetab []byte
@@ -140,11 +155,12 @@ func NewLineTable(data []byte, text uint64) *LineTable {
// isGo12 reports whether this is a Go 1.2 (or later) symbol table.
func (t *LineTable) isGo12() bool {
- t.go12Init()
- return t.go12 == 1
+ t.parsePclnTab()
+ return t.version >= ver12
}
const go12magic = 0xfffffffb
+const go116magic = 0xfffffffa
// uintptr returns the pointer-sized value encoded at b.
// The pointer size is dictated by the table being read.
@@ -155,49 +171,79 @@ func (t *LineTable) uintptr(b []byte) uint64 {
return t.binary.Uint64(b)
}
-// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table.
-func (t *LineTable) go12Init() {
+// parsePclnTab parses the pclntab, setting the version.
+func (t *LineTable) parsePclnTab() {
t.mu.Lock()
defer t.mu.Unlock()
- if t.go12 != 0 {
+ if t.version != verUnknown {
return
}
+ // Note that during this function, setting the version is the last thing we do.
+ // If we set the version too early, and parsing failed (likely as a panic on
+ // slice lookups), we'd have a mistaken version.
+ //
+ // Error paths through this code will default the version to 1.1.
+ t.version = ver11
+
defer func() {
- // If we panic parsing, assume it's not a Go 1.2 symbol table.
+ // If we panic parsing, assume it's a Go 1.1 pclntab.
recover()
}()
// Check header: 4-byte magic, two zeros, pc quantum, pointer size.
- t.go12 = -1 // not Go 1.2 until proven otherwise
if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
(t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) || // pc quantum
(t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
return
}
- switch uint32(go12magic) {
- case binary.LittleEndian.Uint32(t.Data):
- t.binary = binary.LittleEndian
- case binary.BigEndian.Uint32(t.Data):
- t.binary = binary.BigEndian
+ var possibleVersion version
+ leMagic := binary.LittleEndian.Uint32(t.Data)
+ beMagic := binary.BigEndian.Uint32(t.Data)
+ switch {
+ case leMagic == go12magic:
+ t.binary, possibleVersion = binary.LittleEndian, ver12
+ case beMagic == go12magic:
+ t.binary, possibleVersion = binary.BigEndian, ver12
+ case leMagic == go116magic:
+ t.binary, possibleVersion = binary.LittleEndian, ver116
+ case beMagic == go116magic:
+ t.binary, possibleVersion = binary.BigEndian, ver116
default:
return
}
+ // quantum and ptrSize are the same between 1.2 and 1.16
t.quantum = uint32(t.Data[6])
t.ptrsize = uint32(t.Data[7])
- t.nfunctab = uint32(t.uintptr(t.Data[8:]))
- t.functab = t.Data[8+t.ptrsize:]
- functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
- fileoff := t.binary.Uint32(t.functab[functabsize:])
- t.functab = t.functab[:functabsize]
- t.filetab = t.Data[fileoff:]
- t.nfiletab = t.binary.Uint32(t.filetab)
- t.filetab = t.filetab[:t.nfiletab*4]
-
- t.go12 = 1 // so far so good
+ switch possibleVersion {
+ case ver116:
+ t.nfunctab = uint32(t.uintptr(t.Data[8:]))
+ offset := t.uintptr(t.Data[8+t.ptrsize:])
+ t.funcdata = t.Data[offset:]
+ t.functab = t.Data[offset:]
+ functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
+ fileoff := t.binary.Uint32(t.functab[functabsize:])
+ t.filetab = t.functab[fileoff:]
+ t.functab = t.functab[:functabsize]
+ t.nfiletab = t.binary.Uint32(t.filetab)
+ t.filetab = t.filetab[:t.nfiletab*4]
+ case ver12:
+ t.nfunctab = uint32(t.uintptr(t.Data[8:]))
+ t.funcdata = t.Data
+ t.functab = t.Data[8+t.ptrsize:]
+ functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
+ fileoff := t.binary.Uint32(t.functab[functabsize:])
+ t.functab = t.functab[:functabsize]
+ t.filetab = t.Data[fileoff:]
+ t.nfiletab = t.binary.Uint32(t.filetab)
+ t.filetab = t.filetab[:t.nfiletab*4]
+ default:
+ panic("unreachable")
+ }
+ t.version = possibleVersion
}
// go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table.
@@ -213,7 +259,7 @@ 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.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
+ info := t.funcdata[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
f.LineTable = t
f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:]))
f.Sym = &Sym{
@@ -241,7 +287,7 @@ 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.Data[t.uintptr(fm[t.ptrsize:]):]
+ return t.funcdata[t.uintptr(fm[t.ptrsize:]):]
} else if pc < t.uintptr(fm) {
nf = m
} else {
@@ -273,8 +319,8 @@ func (t *LineTable) string(off uint32) string {
if s, ok := t.strings[off]; ok {
return s
}
- i := bytes.IndexByte(t.Data[off:], 0)
- s := string(t.Data[off : off+uint32(i)])
+ i := bytes.IndexByte(t.funcdata[off:], 0)
+ s := string(t.funcdata[off : off+uint32(i)])
t.strings[off] = s
return s
}
@@ -301,7 +347,7 @@ func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
// off is the offset to the beginning of the pc-value table,
// and entry is the start PC for the corresponding function.
func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
- p := t.Data[off:]
+ p := t.funcdata[off:]
val := int32(-1)
pc := entry
@@ -324,8 +370,8 @@ func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum,
return 0
}
- fp := t.Data[filetab:]
- fl := t.Data[linetab:]
+ fp := t.funcdata[filetab:]
+ fl := t.funcdata[linetab:]
fileVal := int32(-1)
filePC := entry
lineVal := int32(-1)
@@ -412,7 +458,7 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
// If this turns out to be a bottleneck, we could build a map[int32][]int32
// mapping file number to a list of functions with code from that file.
for i := uint32(0); i < t.nfunctab; i++ {
- f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
+ 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:])
diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go
index 6baa53defd..33772c7813 100644
--- a/src/debug/gosym/pclntab_test.go
+++ b/src/debug/gosym/pclntab_test.go
@@ -5,6 +5,8 @@
package gosym
import (
+ "bytes"
+ "compress/gzip"
"debug/elf"
"internal/testenv"
"io/ioutil"
@@ -264,3 +266,51 @@ func TestPCLine(t *testing.T) {
off = pc + 1 - text.Addr
}
}
+
+// Test that we can parse a pclntab from 1.15.
+// The file was compiled in /tmp/hello.go:
+// [BEGIN]
+// package main
+//
+// func main() {
+// println("hello")
+// }
+// [END]
+func Test115PclnParsing(t *testing.T) {
+ zippedDat, err := ioutil.ReadFile("testdata/pcln115.gz")
+ if err != nil {
+ t.Fatal(err)
+ }
+ var gzReader *gzip.Reader
+ gzReader, err = gzip.NewReader(bytes.NewBuffer(zippedDat))
+ if err != nil {
+ t.Fatal(err)
+ }
+ var dat []byte
+ dat, err = ioutil.ReadAll(gzReader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ const textStart = 0x1001000
+ pcln := NewLineTable(dat, textStart)
+ var tab *Table
+ tab, err = NewTable(nil, pcln)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var f *Func
+ var pc uint64
+ pc, f, err = tab.LineToPC("/tmp/hello.go", 3)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pcln.version != ver12 {
+ t.Fatal("Expected pcln to parse as an older version")
+ }
+ if pc != 0x105c280 {
+ t.Fatalf("expect pc = 0x105c280, got 0x%x", pc)
+ }
+ if f.Name != "main.main" {
+ t.Fatalf("expected to parse name as main.main, got %v", f.Name)
+ }
+}
diff --git a/src/debug/gosym/testdata/pcln115.gz b/src/debug/gosym/testdata/pcln115.gz
new file mode 100644
index 0000000000..db5c3d48ad
--- /dev/null
+++ b/src/debug/gosym/testdata/pcln115.gz
Binary files differ