proc: refactor dwarf function information loading (#1685)

Splits the code that loads function information from debug_info into
multiple functions.
This makes the changes needed to implement logical breakpoints easier
to make.
This commit is contained in:
Alessandro Arzilli 2019-10-07 18:38:47 +02:00 committed by Derek Parker
parent 36d688bf14
commit ca6c6301cd

@ -80,6 +80,8 @@ type BinaryInfo struct {
// consts[off] lists all the constants with the type defined at offset off.
consts constantsMap
logger *logrus.Entry
}
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
@ -302,7 +304,7 @@ type ElfDynamicSection struct {
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), logger: logflags.DebuggerLogger()}
// TODO: find better way to determine proc arch (perhaps use executable file info).
switch goarch {
@ -1414,74 +1416,20 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
reader.SkipChildren()
case dwarf.TagSubprogram:
ok1 := false
inlined := false
var lowpc, highpc uint64
if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok {
inlined = inval == 1
}
if ranges, _ := image.dwarf.Ranges(entry); len(ranges) == 1 {
ok1 = true
lowpc = ranges[0][0] + image.StaticBase
highpc = ranges[0][1] + image.StaticBase
}
name, ok2 := entry.Val(dwarf.AttrName).(string)
if !ok2 {
if inlined {
bi.addAbstractSubprogram(entry, ctxt, reader, image, cu)
} else {
originOffset, hasAbstractOrigin := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
if hasAbstractOrigin {
name, ok2 = ctxt.abstractOriginNameTable[originOffset]
}
}
var fn Function
if (ok1 == !inlined) && ok2 {
if inlined {
ctxt.abstractOriginNameTable[entry.Offset] = name
}
if !cu.isgo {
name = "C." + name
}
fn = Function{
Name: name,
Entry: lowpc, End: highpc,
offset: entry.Offset,
cu: cu,
}
bi.Functions = append(bi.Functions, fn)
}
if entry.Children {
for {
entry, err = reader.Next()
if err != nil {
image.setLoadError("error reading debug_info: %v", err)
return
}
if entry.Tag == 0 {
break
}
if entry.Tag == dwarf.TagInlinedSubroutine && entry.Val(dwarf.AttrAbstractOrigin) != nil {
originOffset := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
name := ctxt.abstractOriginNameTable[originOffset]
if ranges, _ := image.dwarf.Ranges(entry); len(ranges) == 1 {
ok1 = true
lowpc = ranges[0][0]
highpc = ranges[0][1]
}
callfileidx, ok1 := entry.Val(dwarf.AttrCallFile).(int64)
callline, ok2 := entry.Val(dwarf.AttrCallLine).(int64)
if ok1 && ok2 {
callfile := cu.lineInfo.FileNames[callfileidx-1].Path
cu.concreteInlinedFns = append(cu.concreteInlinedFns, inlinedFn{
Name: name,
LowPC: lowpc + image.StaticBase,
HighPC: highpc + image.StaticBase,
CallFile: callfile,
CallLine: callline,
Parent: &fn,
})
}
}
reader.SkipChildren()
bi.addConcreteInlinedSubprogram(entry, originOffset, ctxt, reader, cu)
} else {
bi.addConcreteSubprogram(entry, ctxt, reader, cu)
}
}
}
@ -1507,6 +1455,179 @@ func (bi *BinaryInfo) loadDebugInfoMapsImportedUnit(entry *dwarf.Entry, ctxt *lo
bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
}
// addAbstractSubprogram adds the abstract entry for an inlined function.
func (bi *BinaryInfo) addAbstractSubprogram(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, reader *reader.Reader, image *Image, cu *compileUnit) {
name, ok := subprogramEntryName(entry, cu)
if !ok {
bi.logger.Errorf("Error reading debug_info: abstract subprogram without name at %#x", entry.Offset)
if entry.Children {
reader.SkipChildren()
}
return
}
fn := Function{
Name: name,
offset: entry.Offset,
cu: cu,
}
if entry.Children {
bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu, &fn)
}
bi.Functions = append(bi.Functions, fn)
ctxt.abstractOriginNameTable[entry.Offset] = name
}
// addConcreteInlinedSubprogram adds the concrete entry of a subprogram that was also inlined.
func (bi *BinaryInfo) addConcreteInlinedSubprogram(entry *dwarf.Entry, originOffset dwarf.Offset, ctxt *loadDebugInfoMapsContext, reader *reader.Reader, cu *compileUnit) {
lowpc, highpc, ok := subprogramEntryRange(entry, cu.image)
if !ok {
bi.logger.Errorf("Error reading debug_info: concrete inlined subprogram without address range at %#x", entry.Offset)
if entry.Children {
reader.SkipChildren()
}
return
}
name, ok := ctxt.abstractOriginNameTable[originOffset]
if !ok {
bi.logger.Errorf("Error reading debug_info: could not find abstract origin of concrete inlined subprogram at %#x (origin offset %#x)", entry.Offset, originOffset)
if entry.Children {
reader.SkipChildren()
}
return
}
fn := Function{
Name: name,
Entry: lowpc, End: highpc,
offset: entry.Offset,
cu: cu,
}
bi.Functions = append(bi.Functions, fn)
if entry.Children {
bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu, &fn)
}
}
// addConcreteSubprogram adds a concrete subprogram (a normal subprogram
// that doesn't have abstract or inlined entries).
func (bi *BinaryInfo) addConcreteSubprogram(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, reader *reader.Reader, cu *compileUnit) {
lowpc, highpc, ok := subprogramEntryRange(entry, cu.image)
if !ok {
bi.logger.Errorf("Error reading debug_info: concrete subprogram without address range at %#x", entry.Offset)
if entry.Children {
reader.SkipChildren()
}
return
}
name, ok := subprogramEntryName(entry, cu)
if !ok {
bi.logger.Errorf("Error reading debug_info: concrete subprogram without name at %#x", entry.Offset)
if entry.Children {
reader.SkipChildren()
}
return
}
fn := Function{
Name: name,
Entry: lowpc,
End: highpc,
offset: entry.Offset,
cu: cu,
}
bi.Functions = append(bi.Functions, fn)
if entry.Children {
bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu, &fn)
}
}
func subprogramEntryName(entry *dwarf.Entry, cu *compileUnit) (string, bool) {
name, ok := entry.Val(dwarf.AttrName).(string)
if !ok {
return "", false
}
if !cu.isgo {
name = "C." + name
}
return name, true
}
func subprogramEntryRange(entry *dwarf.Entry, image *Image) (lowpc, highpc uint64, ok bool) {
ok = false
if ranges, _ := image.dwarf.Ranges(entry); len(ranges) >= 1 {
ok = true
lowpc = ranges[0][0] + image.StaticBase
highpc = ranges[0][1] + image.StaticBase
}
return lowpc, highpc, ok
}
func (bi *BinaryInfo) loadDebugInfoMapsInlinedCalls(ctxt *loadDebugInfoMapsContext, reader *reader.Reader, cu *compileUnit, parentFn *Function) {
for {
entry, err := reader.Next()
if err != nil {
cu.image.setLoadError("error reading debug_info: %v", err)
return
}
switch entry.Tag {
case 0:
return
case dwarf.TagInlinedSubroutine:
originOffset, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
if !ok {
bi.logger.Errorf("Error reading debug_info: inlined call without origin offset at %#x", entry.Offset)
reader.SkipChildren()
continue
}
name, ok := ctxt.abstractOriginNameTable[originOffset]
if !ok {
bi.logger.Errorf("Error reading debug_info: could not find abstract origin (%#x) of inlined call at %#x", originOffset, entry.Offset)
reader.SkipChildren()
continue
}
lowpc, highpc, ok := subprogramEntryRange(entry, cu.image)
if !ok {
bi.logger.Errorf("Error reading debug_info: inlined call without address range at %#x", entry.Offset)
reader.SkipChildren()
continue
}
callfileidx, ok1 := entry.Val(dwarf.AttrCallFile).(int64)
callline, ok2 := entry.Val(dwarf.AttrCallLine).(int64)
if !ok1 || !ok2 {
bi.logger.Errorf("Error reading debug_info: inlined call without CallFile/CallLine at %#x", entry.Offset)
reader.SkipChildren()
continue
}
if int(callfileidx-1) >= len(cu.lineInfo.FileNames) {
bi.logger.Errorf("Error reading debug_info: CallFile (%d) of inlined call does not exist in compile unit file table at %#x", callfileidx, entry.Offset)
reader.SkipChildren()
continue
}
callfile := cu.lineInfo.FileNames[callfileidx-1].Path
cu.concreteInlinedFns = append(cu.concreteInlinedFns, inlinedFn{
Name: name,
LowPC: lowpc,
HighPC: highpc,
CallFile: callfile,
CallLine: callline,
Parent: parentFn,
})
}
reader.SkipChildren()
}
}
func uniq(s []string) []string {
if len(s) <= 0 {
return s