1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:04:43 -07:00

[dev.link] use per package filenames to build pclntab

In order to prevent renumbering of filenames in pclntab generation, use
the per-package file list (previously only used for DWARF generation) as
file-indices. This is the largest step to eliminate renumbering of
filenames in pclntab.

Note, this is probably not the final state of the file table within the
object file. In this form, the linker loads all filenames for all
objects. I'll move to storing the filenames as regular string
symbols,and defaulting all string symbols to using the larger hash value
to make generation of pcln simplest, and most memory friendly.

Change-Id: I23daafa3f4b4535076e23100200ae0e7163aafe0
Reviewed-on: https://go-review.googlesource.com/c/go/+/245485
Run-TryBot: Jeremy Faller <jeremy@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
Jeremy Faller 2020-07-29 11:06:02 -04:00
parent 8370cbe64d
commit 9bdaf99966
16 changed files with 126 additions and 143 deletions

View File

@ -107,7 +107,7 @@ type Func struct {
PCInline Data // PC → inline tree index map
PCData []Data // PC → runtime support data map
FuncData []FuncData // non-PC-specific runtime support data
File []string // paths indexed by PCFile
File map[goobj2.CUFileIndex]struct{} // set of files used in this function
InlTree []InlinedCall
}
@ -123,7 +123,7 @@ type FuncData struct {
// See cmd/internal/obj.InlTree for details.
type InlinedCall struct {
Parent int64
File string
File goobj2.CUFileIndex
Line int64
Func SymID
ParentPC int64
@ -138,7 +138,7 @@ type Package struct {
MaxVersion int64 // maximum Version in any SymID in Syms
Arch string // architecture
Native []*NativeReader // native object data (e.g. ELF)
DWARFFileList []string // List of files for the DWARF .debug_lines section
FileList []string // List of files for this package.
}
type NativeReader struct {

View File

@ -75,6 +75,12 @@ func (r *objReader) readNew() {
// Read things for the current goobj API for now.
// File names
r.p.FileList = make([]string, rr.NFile())
for i := range r.p.FileList {
r.p.FileList[i] = rr.File(i)
}
// Symbols
pcdataBase := start + rr.PcdataBase()
ndef := uint32(rr.NSym() + rr.NHashed64def() + rr.NHasheddef() + rr.NNonpkgdef())
@ -166,7 +172,7 @@ func (r *objReader) readNew() {
PCInline: Data{int64(pcdataBase + info.Pcinline), int64(info.Pcdata[0] - info.Pcinline)},
PCData: make([]Data, len(info.Pcdata)-1), // -1 as we appended one above
FuncData: make([]FuncData, len(info.Funcdataoff)),
File: make([]string, len(info.File)),
File: make(map[goobj2.CUFileIndex]struct{}, len(info.File)),
InlTree: make([]InlinedCall, len(info.InlTree)),
}
sym.Func = f
@ -177,15 +183,14 @@ func (r *objReader) readNew() {
symID := resolveSymRef(funcdata[k])
f.FuncData[k] = FuncData{symID, int64(info.Funcdataoff[k])}
}
for k := range f.File {
symID := resolveSymRef(info.File[k])
f.File[k] = symID.Name
for _, k := range info.File {
f.File[k] = struct{}{}
}
for k := range f.InlTree {
inl := &info.InlTree[k]
f.InlTree[k] = InlinedCall{
Parent: int64(inl.Parent),
File: resolveSymRef(inl.File).Name,
File: inl.File,
Line: int64(inl.Line),
Func: resolveSymRef(inl.Func),
ParentPC: int64(inl.ParentPC),

View File

@ -10,6 +10,10 @@ import (
"encoding/binary"
)
// CUFileIndex is used to index the filenames that are stored in the
// per-package/per-CU FileList.
type CUFileIndex uint32
// FuncInfo is serialized as a symbol (aux symbol). The symbol data is
// the binary encoding of the struct below.
//
@ -26,7 +30,7 @@ type FuncInfo struct {
Pcdata []uint32
PcdataEnd uint32
Funcdataoff []uint32
File []SymRef // TODO: just use string?
File []CUFileIndex
InlTree []InlTreeNode
}
@ -57,8 +61,7 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
}
writeUint32(uint32(len(a.File)))
for _, f := range a.File {
writeUint32(f.PkgIdx)
writeUint32(f.SymIdx)
writeUint32(uint32(f))
}
writeUint32(uint32(len(a.InlTree)))
for i := range a.InlTree {
@ -93,9 +96,9 @@ func (a *FuncInfo) Read(b []byte) {
a.Funcdataoff[i] = readUint32()
}
filelen := readUint32()
a.File = make([]SymRef, filelen)
a.File = make([]CUFileIndex, filelen)
for i := range a.File {
a.File[i] = SymRef{readUint32(), readUint32()}
a.File[i] = CUFileIndex(readUint32())
}
inltreelen := readUint32()
a.InlTree = make([]InlTreeNode, inltreelen)
@ -136,8 +139,7 @@ func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
result.NumFile = binary.LittleEndian.Uint32(b[numfileOff:])
result.FileOff = numfileOff + 4
const symRefSize = 4 + 4
numinltreeOff := result.FileOff + symRefSize*result.NumFile
numinltreeOff := result.FileOff + 4*result.NumFile
result.NumInlTree = binary.LittleEndian.Uint32(b[numinltreeOff:])
result.InlTreeOff = numinltreeOff + 4
@ -181,14 +183,12 @@ func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int
return int64(binary.LittleEndian.Uint32(b[funcdataofffoff+4*k:]))
}
func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) SymRef {
p := binary.LittleEndian.Uint32(b[filesoff+8*k:])
s := binary.LittleEndian.Uint32(b[filesoff+4+8*k:])
return SymRef{p, s}
func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) CUFileIndex {
return CUFileIndex(binary.LittleEndian.Uint32(b[filesoff+4*k:]))
}
func (*FuncInfo) ReadInlTree(b []byte, inltreeoff uint32, k uint32) InlTreeNode {
const inlTreeNodeSize = 4 * 7
const inlTreeNodeSize = 4 * 6
var result InlTreeNode
result.Read(b[inltreeoff+k*inlTreeNodeSize:])
return result
@ -197,7 +197,7 @@ func (*FuncInfo) ReadInlTree(b []byte, inltreeoff uint32, k uint32) InlTreeNode
// InlTreeNode is the serialized form of FileInfo.InlTree.
type InlTreeNode struct {
Parent int32
File SymRef
File CUFileIndex
Line int32
Func SymRef
ParentPC int32
@ -210,8 +210,7 @@ func (inl *InlTreeNode) Write(w *bytes.Buffer) {
w.Write(b[:])
}
writeUint32(uint32(inl.Parent))
writeUint32(inl.File.PkgIdx)
writeUint32(inl.File.SymIdx)
writeUint32(uint32(inl.File))
writeUint32(uint32(inl.Line))
writeUint32(inl.Func.PkgIdx)
writeUint32(inl.Func.SymIdx)
@ -226,7 +225,7 @@ func (inl *InlTreeNode) Read(b []byte) []byte {
return x
}
inl.Parent = int32(readUint32())
inl.File = SymRef{readUint32(), readUint32()}
inl.File = CUFileIndex(readUint32())
inl.Line = int32(readUint32())
inl.Func = SymRef{readUint32(), readUint32()}
inl.ParentPC = int32(readUint32())

View File

@ -38,7 +38,7 @@ import (
//
// PkgIndex [...]string // referenced packages by index
//
// DwarfFiles [...]string
// Files [...]string
//
// SymbolDefs [...]struct {
// Name string
@ -177,7 +177,7 @@ const (
const (
BlkAutolib = iota
BlkPkgIdx
BlkDwarfFile
BlkFile
BlkSymdef
BlkHashed64def
BlkHasheddef
@ -686,12 +686,12 @@ func (r *Reader) Pkg(i int) string {
return r.StringRef(off)
}
func (r *Reader) NDwarfFile() int {
return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / stringRefSize
func (r *Reader) NFile() int {
return int(r.h.Offsets[BlkFile+1]-r.h.Offsets[BlkFile]) / stringRefSize
}
func (r *Reader) DwarfFile(i int) string {
off := r.h.Offsets[BlkDwarfFile] + uint32(i)*stringRefSize
func (r *Reader) File(i int) string {
off := r.h.Offsets[BlkFile] + uint32(i)*stringRefSize
return r.StringRef(off)
}

View File

@ -32,18 +32,6 @@ const (
func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
dctxt := dwCtxt{ctxt}
// The Pcfile table is used to generate the debug_lines section, and the file
// indices for that data could differ from the files we write out for the
// debug_lines section. Here we generate a LUT between those two indices.
fileNums := make(map[int32]int64)
for i, filename := range s.Func.Pcln.File {
if symbolIndex := ctxt.PosTable.FileIndex(filename); symbolIndex >= 0 {
fileNums[int32(i)] = int64(symbolIndex) + 1
} else {
panic(fmt.Sprintf("First time we've seen filename: %q", filename))
}
}
// Emit a LNE_set_address extended opcode, so as to establish the
// starting text address of this function.
dctxt.AddUint8(lines, 0)

View File

@ -22,3 +22,9 @@ func linkgetlineFromPos(ctxt *Link, xpos src.XPos) (f string, l int32) {
// TODO(gri) Should this use relative or absolute line number?
return pos.SymFilename(), int32(pos.RelLine())
}
// getFileIndexAndLine returns the file index (local to the CU), and the line number for a position.
func getFileIndexAndLine(ctxt *Link, xpos src.XPos) (int, int32) {
f, l := linkgetlineFromPos(ctxt, xpos)
return ctxt.PosTable.FileIndex(f), l
}

View File

@ -631,9 +631,7 @@ type Pcln struct {
Pcdata []Pcdata
Funcdata []*LSym
Funcdataoff []int64
File []string
Lastfile string
Lastindex int
UsedFiles map[goobj2.CUFileIndex]struct{} // file indices used while generating pcfile
InlTree InlTree // per-function inlining tree extracted from the global tree
}

View File

@ -16,6 +16,7 @@ import (
"fmt"
"io"
"path/filepath"
"sort"
"strings"
)
@ -69,9 +70,9 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) {
w.StringRef(pkg)
}
// DWARF file table
h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
for _, f := range ctxt.PosTable.DebugLinesFileTable() {
// File table (for DWARF and pcln generation).
h.Offsets[goobj2.BlkFile] = w.Offset()
for _, f := range ctxt.PosTable.FileTable() {
w.StringRef(filepath.ToSlash(f))
}
@ -248,20 +249,9 @@ func (w *writer) StringTable() {
}
w.AddString(s.Name)
})
w.ctxt.traverseSyms(traverseDefs, func(s *LSym) {
if s.Type != objabi.STEXT {
return
}
pc := &s.Func.Pcln
for _, f := range pc.File {
w.AddString(filepath.ToSlash(f))
}
for _, call := range pc.InlTree.nodes {
f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
w.AddString(filepath.ToSlash(f))
}
})
for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
// All filenames are in the postable.
for _, f := range w.ctxt.PosTable.FileTable() {
w.AddString(filepath.ToSlash(f))
}
}
@ -594,18 +584,19 @@ func genFuncInfoSyms(ctxt *Link) {
for i, x := range pc.Funcdataoff {
o.Funcdataoff[i] = uint32(x)
}
o.File = make([]goobj2.SymRef, len(pc.File))
for i, f := range pc.File {
fsym := ctxt.Lookup(f)
o.File[i] = makeSymRef(fsym)
i := 0
o.File = make([]goobj2.CUFileIndex, len(pc.UsedFiles))
for f := range pc.UsedFiles {
o.File[i] = f
i++
}
sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
o.InlTree = make([]goobj2.InlTreeNode, len(pc.InlTree.nodes))
for i, inl := range pc.InlTree.nodes {
f, l := linkgetlineFromPos(ctxt, inl.Pos)
fsym := ctxt.Lookup(f)
f, l := getFileIndexAndLine(ctxt, inl.Pos)
o.InlTree[i] = goobj2.InlTreeNode{
Parent: int32(inl.Parent),
File: makeSymRef(fsym),
File: goobj2.CUFileIndex(f),
Line: l,
Func: makeSymRef(inl.Func),
ParentPC: inl.ParentPC,

View File

@ -5,6 +5,7 @@
package obj
import (
"cmd/internal/goobj2"
"encoding/binary"
"log"
)
@ -130,28 +131,13 @@ func pctofileline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg
if p.As == ATEXT || p.As == ANOP || p.Pos.Line() == 0 || phase == 1 {
return oldval
}
f, l := linkgetlineFromPos(ctxt, p.Pos)
f, l := getFileIndexAndLine(ctxt, p.Pos)
if arg == nil {
return l
}
pcln := arg.(*Pcln)
if f == pcln.Lastfile {
return int32(pcln.Lastindex)
}
for i, file := range pcln.File {
if file == f {
pcln.Lastfile = f
pcln.Lastindex = i
return int32(i)
}
}
i := len(pcln.File)
pcln.File = append(pcln.File, f)
pcln.Lastfile = f
pcln.Lastindex = i
return int32(i)
pcln.UsedFiles[goobj2.CUFileIndex(f)] = struct{}{}
return int32(f)
}
// pcinlineState holds the state used to create a function's inlining
@ -263,6 +249,7 @@ func pctopcdata(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg i
func linkpcln(ctxt *Link, cursym *LSym) {
pcln := &cursym.Func.Pcln
pcln.UsedFiles = make(map[goobj2.CUFileIndex]struct{})
npcdata := 0
nfuncdata := 0

View File

@ -367,8 +367,9 @@ func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent
fn(fsym, d)
}
}
for _, f := range pc.File {
if filesym := ctxt.Lookup(f); filesym != nil {
files := ctxt.PosTable.FileTable()
for f := range pc.UsedFiles {
if filesym := ctxt.Lookup(files[f]); filesym != nil {
fn(fsym, filesym)
}
}

View File

@ -135,7 +135,7 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
return "", 0, nil
}
fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch))
fileName := s.Func.File[fileID]
fileName := f.goobj.FileList[fileID]
pcline := make([]byte, s.Func.PCLine.Size)
_, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
if err != nil {

View File

@ -163,8 +163,8 @@ func (t *PosTable) FileIndex(filename string) int {
return -1
}
// DebugLinesFiles returns the file table for the debug_lines DWARF section.
func (t *PosTable) DebugLinesFileTable() []string {
// FileTable returns a slice of all files used to build this package.
func (t *PosTable) FileTable() []string {
// Create a LUT of the global package level file indices. This table is what
// is written in the debug_lines header, the file[N] will be referenced as
// N+1 in the debug_lines table.

View File

@ -1224,7 +1224,7 @@ func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []
// Copy over the file table.
fileNums := make(map[string]int)
for i, name := range unit.DWARFFileTable {
for i, name := range unit.FileTable {
name := expandFile(name)
if len(name) == 0 {
// Can't have empty filenames, and having a unique

View File

@ -5,6 +5,7 @@
package ld
import (
"cmd/internal/goobj2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
@ -32,7 +33,7 @@ import (
type oldPclnState struct {
ldr *loader.Loader
deferReturnSym loader.Sym
numberedFiles map[loader.Sym]int64
numberedFiles map[string]int64
filepaths []string
}
@ -88,7 +89,7 @@ func makeOldPclnState(ctxt *Link) *oldPclnState {
state := &oldPclnState{
ldr: ldr,
deferReturnSym: drs,
numberedFiles: make(map[loader.Sym]int64),
numberedFiles: make(map[string]int64),
// NB: initial entry in filepaths below is to reserve the zero value,
// so that when we do a map lookup in numberedFiles fails, it will not
// return a value slot in filepaths.
@ -153,30 +154,37 @@ func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
}
// numberfile assigns a file number to the file if it hasn't been assigned already.
func (state *oldPclnState) numberfile(file loader.Sym) int64 {
// This funciton looks at a CU's file at index [i], and if it's a new filename,
// stores that filename in the global file table, and adds it to the map lookup
// for renumbering pcfile.
func (state *oldPclnState) numberfile(cu *sym.CompilationUnit, i goobj2.CUFileIndex) int64 {
file := cu.FileTable[i]
if val, ok := state.numberedFiles[file]; ok {
return val
}
sn := state.ldr.SymName(file)
path := sn[len(src.FileSymPrefix):]
path := file
if strings.HasPrefix(path, src.FileSymPrefix) {
path = file[len(src.FileSymPrefix):]
}
val := int64(len(state.filepaths))
state.numberedFiles[file] = val
state.filepaths = append(state.filepaths, expandGoroot(path))
return val
}
func (state *oldPclnState) fileVal(file loader.Sym) int64 {
func (state *oldPclnState) fileVal(cu *sym.CompilationUnit, i int32) int64 {
file := cu.FileTable[i]
if val, ok := state.numberedFiles[file]; ok {
return val
}
panic("should have been numbered first")
}
func (state *oldPclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.Pcdata) {
func (state *oldPclnState) renumberfiles(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, d *sym.Pcdata) {
// Give files numbers.
nf := fi.NumFile()
for i := uint32(0); i < nf; i++ {
state.numberfile(fi.File(int(i)))
state.numberfile(cu, fi.File(int(i)))
}
buf := make([]byte, binary.MaxVarintLen32)
@ -191,10 +199,10 @@ func (state *oldPclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.
if oldval == -1 {
val = -1
} else {
if oldval < 0 || oldval >= int32(nf) {
if oldval < 0 || oldval >= int32(len(cu.FileTable)) {
log.Fatalf("bad pcdata %d", oldval)
}
val = int32(state.fileVal(fi.File(int(oldval))))
val = int32(state.fileVal(cu, oldval))
}
dv := val - newval
@ -287,7 +295,7 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// genInlTreeSym generates the InlTree sym for a function with the
// specified FuncInfo.
func (state *oldPclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym {
func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym {
ldr := state.ldr
its := ldr.CreateExtSym("", 0)
inlTreeSym := ldr.MakeSymbolUpdater(its)
@ -305,7 +313,7 @@ func (state *oldPclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch, new
// might overlap exactly so that only the innermost file
// appears in the Pcfile table. In that case, this assigns
// the outer file a number.
val := state.numberfile(call.File)
val := state.numberfile(cu, call.File)
nameoff, ok := newState.funcNameOffset[call.Func]
if !ok {
panic("couldn't find function name offset")
@ -603,11 +611,12 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
deferreturn := oldState.computeDeferReturn(&ctxt.Target, s)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
cu := ldr.SymUnit(s)
if fi.Valid() {
pcsp = sym.Pcdata{P: fi.Pcsp()}
pcfile = sym.Pcdata{P: fi.Pcfile()}
pcline = sym.Pcdata{P: fi.Pcline()}
oldState.renumberfiles(ctxt, fi, &pcfile)
oldState.renumberfiles(ctxt, cu, fi, &pcfile)
if false {
// Sanity check the new numbering
it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
@ -621,7 +630,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
}
if fi.Valid() && fi.NumInlTree() > 0 {
its := oldState.genInlTreeSym(fi, ctxt.Arch, state)
its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
funcdata[objabi.FUNCDATA_InlTree] = its
pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
}

View File

@ -1961,17 +1961,16 @@ func (fi *FuncInfo) NumFile() uint32 {
return fi.lengths.NumFile
}
func (fi *FuncInfo) File(k int) Sym {
func (fi *FuncInfo) File(k int) goobj2.CUFileIndex {
if !fi.lengths.Initialized {
panic("need to call Preload first")
}
sr := (*goobj2.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
return fi.l.resolve(fi.r, sr)
return (*goobj2.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
}
type InlTreeNode struct {
Parent int32
File Sym
File goobj2.CUFileIndex
Line int32
Func Sym
ParentPC int32
@ -1991,7 +1990,7 @@ func (fi *FuncInfo) InlTree(k int) InlTreeNode {
node := (*goobj2.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
return InlTreeNode{
Parent: node.Parent,
File: fi.l.resolve(fi.r, node.File),
File: node.File,
Line: node.Line,
Func: fi.l.resolve(fi.r, node.Func),
ParentPC: node.ParentPC,
@ -2060,10 +2059,10 @@ func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, u
lib.Autolib = append(lib.Autolib, r.Autolib()...)
// DWARF file table
nfile := r.NDwarfFile()
unit.DWARFFileTable = make([]string, nfile)
for i := range unit.DWARFFileTable {
unit.DWARFFileTable[i] = r.DwarfFile(i)
nfile := r.NFile()
unit.FileTable = make([]string, nfile)
for i := range unit.FileTable {
unit.FileTable[i] = r.File(i)
}
l.addObj(lib.Pkg, or)

View File

@ -25,7 +25,7 @@ type CompilationUnit struct {
PclnIndex int // Index of this CU in pclntab
PCs []dwarf.Range // PC ranges, relative to Textp[0]
DWInfo *dwarf.DWDie // CU root DIE
DWARFFileTable []string // The file table used to generate the .debug_lines
FileTable []string // The file table used in this compilation unit.
Consts LoaderSym // Package constants DIEs
FuncDIEs []LoaderSym // Function DIE subtrees