提交 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
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
type compileUnit struct {
entry *dwarf.Entry // debug_info entry describing this compile unit
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
Name string // univocal name for non-go compile units
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 {
......@@ -102,6 +104,16 @@ type partialUnit struct {
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.
type Function struct {
Name string
......@@ -320,6 +332,17 @@ func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Func
for _, cu := range bi.compileUnits {
if cu.lineInfo.Lookup[filename] != nil {
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)
if fn != nil {
return
......
......@@ -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) {
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")
......
......@@ -195,6 +195,8 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
var cu *compileUnit = nil
var pu *partialUnit = nil
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() {
if err != nil {
break
......@@ -365,35 +367,77 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
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, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 {
ok1 = true
lowpc = ranges[0][0]
highpc = ranges[0][1]
}
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 {
pu.functions = append(pu.functions, Function{
fn = Function{
Name: name,
Entry: lowpc, End: highpc,
offset: entry.Offset,
cu: &compileUnit{},
})
}
pu.functions = append(pu.functions, fn)
} else {
if !cu.isgo {
name = "C." + name
}
bi.Functions = append(bi.Functions, Function{
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 {
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))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册