提交 49bfbe6d 编写于 作者: D Derek Parker 提交者: Alessandro Arzilli

proc: Increase inline function support

This patch makes it so inlined functions are returned in the
function
list, and also allows users to set breakpoints on the call site of
inlined functions.

Fixes #1261
上级 568251e4
...@@ -79,13 +79,15 @@ var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwi ...@@ -79,13 +79,15 @@ var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwi
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231) const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
type compileUnit struct { type compileUnit struct {
entry *dwarf.Entry // debug_info entry describing this compile unit Name string // univocal name for non-go compile units
isgo bool // true if this is the go compile unit
Name string // univocal name for non-go compile units
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
LowPC, HighPC uint64 LowPC, HighPC uint64
optimized bool // this compile unit is optimized
producer string // producer attribute entry *dwarf.Entry // debug_info entry describing this compile unit
isgo bool // true if this is the go compile unit
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
concreteInlinedFns []inlinedFn // list of concrete inlined functions within this compile unit
optimized bool // this compile unit is optimized
producer string // producer attribute
} }
type partialUnitConstant struct { type partialUnitConstant struct {
...@@ -102,6 +104,16 @@ type partialUnit struct { ...@@ -102,6 +104,16 @@ type partialUnit struct {
functions []Function functions []Function
} }
// inlinedFn represents a concrete inlined function, e.g.
// an entry for the generated code of an inlined function.
type inlinedFn struct {
Name string // Name of the function that was inlined
LowPC, HighPC uint64 // Address range of the generated inlined instructions
CallFile string // File of the call site of the inlined function
CallLine int64 // Line of the call site of the inlined function
Parent *Function // The function that contains this inlined function
}
// Function describes a function in the target program. // Function describes a function in the target program.
type Function struct { type Function struct {
Name string Name string
...@@ -320,6 +332,17 @@ func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Func ...@@ -320,6 +332,17 @@ func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Func
for _, cu := range bi.compileUnits { for _, cu := range bi.compileUnits {
if cu.lineInfo.Lookup[filename] != nil { if cu.lineInfo.Lookup[filename] != nil {
pc = cu.lineInfo.LineToPC(filename, lineno) pc = cu.lineInfo.LineToPC(filename, lineno)
if pc == 0 {
// Check to see if this file:line belongs to the call site
// of an inlined function.
for _, ifn := range cu.concreteInlinedFns {
if strings.Contains(ifn.CallFile, filename) && ifn.CallLine == int64(lineno) {
pc = ifn.LowPC
fn = ifn.Parent
return
}
}
}
fn = bi.PCToFunc(pc) fn = bi.PCToFunc(pc)
if fn != nil { if fn != nil {
return return
......
...@@ -3756,6 +3756,48 @@ func TestInlineStepOut(t *testing.T) { ...@@ -3756,6 +3756,48 @@ func TestInlineStepOut(t *testing.T) {
}) })
} }
func TestInlineFunctionList(t *testing.T) {
// We should be able to list all functions, even inlined ones.
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p proc.Process, fixture protest.Fixture) {
var found bool
for _, fn := range p.BinInfo().Functions {
if strings.Contains(fn.Name, "inlineThis") {
found = true
break
}
}
if !found {
t.Fatal("inline function not returned")
}
})
}
func TestInlineBreakpoint(t *testing.T) {
// We should be able to set a breakpoint on the call site of an inlined function.
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p proc.Process, fixture protest.Fixture) {
pc, fn, err := p.BinInfo().LineToPC(fixture.Source, 17)
if pc == 0 {
t.Fatal("unable to get PC for inlined function call")
}
expectedFn := "main.main"
if fn.Name != expectedFn {
t.Fatalf("incorrect function returned, expected %s, got %s", expectedFn, fn.Name)
}
_, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("unable to set breakpoint: %v", err)
}
})
}
func TestIssue951(t *testing.T) { func TestIssue951(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
t.Skip("scopes not implemented in <=go1.8") t.Skip("scopes not implemented in <=go1.8")
......
...@@ -195,6 +195,8 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou ...@@ -195,6 +195,8 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
var cu *compileUnit = nil var cu *compileUnit = nil
var pu *partialUnit = nil var pu *partialUnit = nil
var partialUnits = make(map[dwarf.Offset]*partialUnit) var partialUnits = make(map[dwarf.Offset]*partialUnit)
abstractOriginNameTable := make(map[dwarf.Offset]string)
outer:
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil { if err != nil {
break break
...@@ -365,35 +367,77 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou ...@@ -365,35 +367,77 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
case dwarf.TagSubprogram: case dwarf.TagSubprogram:
ok1 := false ok1 := false
inlined := false
var lowpc, highpc uint64 var lowpc, highpc uint64
if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok {
inlined = inval == 1
}
if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 { if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 {
ok1 = true ok1 = true
lowpc = ranges[0][0] lowpc = ranges[0][0]
highpc = ranges[0][1] highpc = ranges[0][1]
} }
name, ok2 := entry.Val(dwarf.AttrName).(string) name, ok2 := entry.Val(dwarf.AttrName).(string)
if ok1 && ok2 { var fn Function
if (ok1 == !inlined) && ok2 {
if inlined {
abstractOriginNameTable[entry.Offset] = name
}
if pu != nil { if pu != nil {
pu.functions = append(pu.functions, Function{ fn = Function{
Name: name, Name: name,
Entry: lowpc, End: highpc, Entry: lowpc, End: highpc,
offset: entry.Offset, offset: entry.Offset,
cu: &compileUnit{}, cu: &compileUnit{},
}) }
pu.functions = append(pu.functions, fn)
} else { } else {
if !cu.isgo { if !cu.isgo {
name = "C." + name name = "C." + name
} }
bi.Functions = append(bi.Functions, Function{ fn = Function{
Name: name, Name: name,
Entry: lowpc, End: highpc, Entry: lowpc, End: highpc,
offset: entry.Offset, offset: entry.Offset,
cu: cu, cu: cu,
}) }
bi.Functions = append(bi.Functions, fn)
}
}
if entry.Children {
for {
entry, err = reader.Next()
if err != nil {
break outer
}
if entry.Tag == 0 {
break
}
if entry.Tag == dwarf.TagInlinedSubroutine {
originOffset := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
name := abstractOriginNameTable[originOffset]
if ranges, _ := bi.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,
HighPC: highpc,
CallFile: callfile,
CallLine: callline,
Parent: &fn,
})
}
}
reader.SkipChildren()
} }
} }
reader.SkipChildren()
} }
} }
sort.Sort(compileUnitsByLowpc(bi.compileUnits)) sort.Sort(compileUnitsByLowpc(bi.compileUnits))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册