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:
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[off] lists all the constants with the type defined at offset off.
|
||||||
consts constantsMap
|
consts constantsMap
|
||||||
|
|
||||||
|
logger *logrus.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
// 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.
|
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
||||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
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).
|
// TODO: find better way to determine proc arch (perhaps use executable file info).
|
||||||
switch goarch {
|
switch goarch {
|
||||||
@ -1414,74 +1416,20 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
|
|||||||
reader.SkipChildren()
|
reader.SkipChildren()
|
||||||
|
|
||||||
case dwarf.TagSubprogram:
|
case dwarf.TagSubprogram:
|
||||||
ok1 := false
|
|
||||||
inlined := false
|
inlined := false
|
||||||
var lowpc, highpc uint64
|
|
||||||
if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok {
|
if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok {
|
||||||
inlined = inval == 1
|
inlined = inval == 1
|
||||||
}
|
}
|
||||||
if ranges, _ := image.dwarf.Ranges(entry); len(ranges) == 1 {
|
|
||||||
ok1 = true
|
if inlined {
|
||||||
lowpc = ranges[0][0] + image.StaticBase
|
bi.addAbstractSubprogram(entry, ctxt, reader, image, cu)
|
||||||
highpc = ranges[0][1] + image.StaticBase
|
} else {
|
||||||
}
|
|
||||||
name, ok2 := entry.Val(dwarf.AttrName).(string)
|
|
||||||
if !ok2 {
|
|
||||||
originOffset, hasAbstractOrigin := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
|
originOffset, hasAbstractOrigin := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
|
||||||
if hasAbstractOrigin {
|
if hasAbstractOrigin {
|
||||||
name, ok2 = ctxt.abstractOriginNameTable[originOffset]
|
bi.addConcreteInlinedSubprogram(entry, originOffset, ctxt, reader, cu)
|
||||||
}
|
} else {
|
||||||
}
|
bi.addConcreteSubprogram(entry, ctxt, reader, cu)
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1507,6 +1455,179 @@ func (bi *BinaryInfo) loadDebugInfoMapsImportedUnit(entry *dwarf.Entry, ctxt *lo
|
|||||||
bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
|
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 {
|
func uniq(s []string) []string {
|
||||||
if len(s) <= 0 {
|
if len(s) <= 0 {
|
||||||
return s
|
return s
|
||||||
|
Loading…
Reference in New Issue
Block a user