proc: support multiple functions with the same name (#3297)
The compiler produces ABI compatibility wrappers for some functions. We have changed the support for breakpoints to allow a single logical breakpoint to correspond to multiple physical breakpoints, take advantage of that to set breakpoints on both the ABI wrapper and the real function. Fixes #3296
This commit is contained in:
parent
1522382336
commit
3507ff977a
@ -450,7 +450,8 @@ func (loc *NormalLocationSpec) findFuncCandidates(bi *proc.BinaryInfo, limit int
|
||||
}
|
||||
candidateFuncs[fname] = struct{}{}
|
||||
}
|
||||
for _, f := range bi.LookupFunc {
|
||||
for _, fns := range bi.LookupFunc() {
|
||||
f := fns[0]
|
||||
if len(candidateFuncs) >= limit {
|
||||
break
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func AMD64Arch(goos string) *Arch {
|
||||
func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
|
||||
a := bi.Arch
|
||||
if a.sigreturnfn == nil {
|
||||
a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
|
||||
a.sigreturnfn = bi.lookupOneFunc("runtime.sigreturn")
|
||||
}
|
||||
|
||||
if fctxt == nil || (a.sigreturnfn != nil && pc >= a.sigreturnfn.Entry && pc < a.sigreturnfn.End) {
|
||||
@ -96,7 +96,7 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
|
||||
}
|
||||
|
||||
if a.crosscall2fn == nil {
|
||||
a.crosscall2fn = bi.LookupFunc["crosscall2"]
|
||||
a.crosscall2fn = bi.lookupOneFunc("crosscall2")
|
||||
}
|
||||
|
||||
if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End {
|
||||
|
@ -60,7 +60,7 @@ func ARM64Arch(goos string) *Arch {
|
||||
func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
|
||||
a := bi.Arch
|
||||
if a.sigreturnfn == nil {
|
||||
a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
|
||||
a.sigreturnfn = bi.lookupOneFunc("runtime.sigreturn")
|
||||
}
|
||||
|
||||
if fctxt == nil || (a.sigreturnfn != nil && pc >= a.sigreturnfn.Entry && pc < a.sigreturnfn.End) {
|
||||
@ -107,7 +107,7 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
|
||||
}
|
||||
|
||||
if a.crosscall2fn == nil {
|
||||
a.crosscall2fn = bi.LookupFunc["crosscall2"]
|
||||
a.crosscall2fn = bi.lookupOneFunc("crosscall2")
|
||||
}
|
||||
|
||||
if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End {
|
||||
|
@ -60,8 +60,8 @@ type BinaryInfo struct {
|
||||
Functions []Function
|
||||
// Sources is a list of all source files found in debug_line.
|
||||
Sources []string
|
||||
// LookupFunc maps function names to a description of the function.
|
||||
LookupFunc map[string]*Function
|
||||
// lookupFunc maps function names to a description of the function.
|
||||
lookupFunc map[string][]*Function
|
||||
// lookupGenericFunc maps function names, with their type parameters removed, to functions.
|
||||
// Functions that are not generic are not added to this map.
|
||||
lookupGenericFunc map[string][]*Function
|
||||
@ -311,8 +311,8 @@ func allInlineCallRanges(tree *godwarf.Tree) []inlRange {
|
||||
|
||||
// FindFunction returns the functions with name funcName.
|
||||
func (bi *BinaryInfo) FindFunction(funcName string) ([]*Function, error) {
|
||||
if fn := bi.LookupFunc[funcName]; fn != nil {
|
||||
return []*Function{fn}, nil
|
||||
if fns := bi.LookupFunc()[funcName]; fns != nil {
|
||||
return fns, nil
|
||||
}
|
||||
fns := bi.LookupGenericFunc()[funcName]
|
||||
if len(fns) == 0 {
|
||||
@ -393,7 +393,7 @@ func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error
|
||||
}
|
||||
|
||||
func findRetPC(t *Target, name string) ([]uint64, error) {
|
||||
fn := t.BinInfo().LookupFunc[name]
|
||||
fn := t.BinInfo().lookupOneFunc(name)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not find %s", name)
|
||||
}
|
||||
@ -2111,11 +2111,8 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugInfoBytes, debugLineB
|
||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
||||
sort.Sort(packageVarsByAddr(bi.packageVars))
|
||||
|
||||
bi.LookupFunc = make(map[string]*Function)
|
||||
bi.lookupFunc = nil
|
||||
bi.lookupGenericFunc = nil
|
||||
for i := range bi.Functions {
|
||||
bi.LookupFunc[bi.Functions[i].Name] = &bi.Functions[i]
|
||||
}
|
||||
|
||||
for _, cu := range image.compileUnits {
|
||||
if cu.lineInfo != nil {
|
||||
@ -2129,7 +2126,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugInfoBytes, debugLineB
|
||||
|
||||
if bi.regabi {
|
||||
// prepare patch for runtime.mallocgc's DIE
|
||||
fn := bi.LookupFunc["runtime.mallocgc"]
|
||||
fn := bi.lookupOneFunc("runtime.mallocgc")
|
||||
if fn != nil && fn.cu.image == image {
|
||||
tree, err := image.getDwarfTree(fn.offset)
|
||||
if err == nil {
|
||||
@ -2166,6 +2163,25 @@ func (bi *BinaryInfo) LookupGenericFunc() map[string][]*Function {
|
||||
return bi.lookupGenericFunc
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) LookupFunc() map[string][]*Function {
|
||||
if bi.lookupFunc == nil {
|
||||
bi.lookupFunc = make(map[string][]*Function)
|
||||
for i := range bi.Functions {
|
||||
name := bi.Functions[i].Name
|
||||
bi.lookupFunc[name] = append(bi.lookupFunc[name], &bi.Functions[i])
|
||||
}
|
||||
}
|
||||
return bi.lookupFunc
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) lookupOneFunc(name string) *Function {
|
||||
fns := bi.LookupFunc()[name]
|
||||
if fns == nil {
|
||||
return nil
|
||||
}
|
||||
return fns[0]
|
||||
}
|
||||
|
||||
// loadDebugInfoMapsCompileUnit loads entry from a single compile unit.
|
||||
func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) {
|
||||
hasAttrGoPkgName := goversion.ProducerAfterOrEqual(cu.producer, 1, 13)
|
||||
|
@ -674,8 +674,13 @@ func (t *Target) setBreakpointInternal(logicalID int, addr uint64, kind Breakpoi
|
||||
if breaklet != nil && breaklet.Cond == nil {
|
||||
breaklet.Cond = lbp.Cond
|
||||
}
|
||||
lbp.File = bp.File
|
||||
lbp.Line = bp.Line
|
||||
if lbp.File == "" && lbp.Line == 0 {
|
||||
lbp.File = bp.File
|
||||
lbp.Line = bp.Line
|
||||
} else if bp.File != lbp.File || bp.Line != lbp.Line {
|
||||
lbp.File = "<multiple locations>"
|
||||
lbp.Line = 0
|
||||
}
|
||||
fn := t.BinInfo().PCToFunc(bp.Addr)
|
||||
if fn != nil {
|
||||
lbp.FunctionName = fn.NameWithoutTypeParams()
|
||||
|
@ -153,7 +153,7 @@ func TestDwarfExprRegisters(t *testing.T) {
|
||||
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mainfn := bi.LookupFunc["main.main"]
|
||||
mainfn := bi.LookupFunc()["main.main"][0]
|
||||
mem := newFakeMemory(fakeCFA(), uint64(0), uint64(testCases["b"]))
|
||||
regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
|
||||
regs.Regs.Rax = uint64(testCases["a"])
|
||||
@ -215,7 +215,7 @@ func TestDwarfExprComposite(t *testing.T) {
|
||||
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mainfn := bi.LookupFunc["main.main"]
|
||||
mainfn := bi.LookupFunc()["main.main"][0]
|
||||
|
||||
mem := newFakeMemory(fakeCFA(), uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal))
|
||||
var regs linutil.AMD64Registers
|
||||
@ -289,7 +289,7 @@ func TestDwarfExprLoclist(t *testing.T) {
|
||||
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mainfn := bi.LookupFunc["main.main"]
|
||||
mainfn := bi.LookupFunc()["main.main"][0]
|
||||
|
||||
mem := newFakeMemory(fakeCFA(), uint16(before), uint16(after))
|
||||
const PC = 0x40100
|
||||
@ -326,7 +326,7 @@ func TestIssue1419(t *testing.T) {
|
||||
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mainfn := bi.LookupFunc["main.main"]
|
||||
mainfn := bi.LookupFunc()["main.main"][0]
|
||||
|
||||
mem := newFakeMemory(fakeCFA())
|
||||
|
||||
|
@ -2280,8 +2280,8 @@ func (v *Variable) findMethod(mname string) (*Variable, error) {
|
||||
|
||||
//TODO(aarzilli): support generic functions?
|
||||
|
||||
if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.%s.%s", pkg, receiver, mname)]; ok {
|
||||
r, err := functionToVariable(fn, v.bi, v.mem)
|
||||
if fns := v.bi.LookupFunc()[fmt.Sprintf("%s.%s.%s", pkg, receiver, mname)]; len(fns) == 1 {
|
||||
r, err := functionToVariable(fns[0], v.bi, v.mem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -2293,8 +2293,8 @@ func (v *Variable) findMethod(mname string) (*Variable, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.(*%s).%s", pkg, receiver, mname)]; ok {
|
||||
r, err := functionToVariable(fn, v.bi, v.mem)
|
||||
if fns := v.bi.LookupFunc()[fmt.Sprintf("%s.(*%s).%s", pkg, receiver, mname)]; len(fns) == 1 {
|
||||
r, err := functionToVariable(fns[0], v.bi, v.mem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1211,8 +1211,8 @@ func findCallInjectionStateForThread(t *Target, thread Thread) (*G, *callInjecti
|
||||
func debugCallFunction(bi *BinaryInfo) (*Function, int) {
|
||||
for version := maxDebugCallVersion; version >= 1; version-- {
|
||||
name := debugCallFunctionNamePrefix2 + "V" + strconv.Itoa(version)
|
||||
fn, ok := bi.LookupFunc[name]
|
||||
if ok && fn != nil {
|
||||
fn := bi.lookupOneFunc(name)
|
||||
if fn != nil {
|
||||
return fn, version
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func I386Arch(goos string) *Arch {
|
||||
func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
|
||||
i := bi.Arch
|
||||
if i.sigreturnfn == nil {
|
||||
i.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
|
||||
i.sigreturnfn = bi.lookupOneFunc("runtime.sigreturn")
|
||||
}
|
||||
|
||||
if fctxt == nil || (i.sigreturnfn != nil && pc >= i.sigreturnfn.Entry && pc < i.sigreturnfn.End) {
|
||||
@ -90,7 +90,7 @@ func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryI
|
||||
}
|
||||
|
||||
if i.crosscall2fn == nil {
|
||||
i.crosscall2fn = bi.LookupFunc["crosscall2"]
|
||||
i.crosscall2fn = bi.lookupOneFunc("crosscall2")
|
||||
}
|
||||
|
||||
// TODO(chainhelen), need to check whether there is a bad frame descriptor like amd64.
|
||||
|
@ -827,10 +827,11 @@ func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf
|
||||
return errors.New("too many arguments in traced function, max is 12 input+return")
|
||||
}
|
||||
|
||||
fn, ok := dbp.bi.LookupFunc[fnName]
|
||||
if !ok {
|
||||
fns := dbp.bi.LookupFunc()[fnName]
|
||||
if len(fns) != 1 {
|
||||
return fmt.Errorf("could not find function: %s", fnName)
|
||||
}
|
||||
fn := fns[0]
|
||||
|
||||
offset, err := dbp.BinInfo().GStructOffset(dbp.Memory())
|
||||
if err != nil {
|
||||
|
@ -209,23 +209,35 @@ func TestExitAfterContinue(t *testing.T) {
|
||||
}
|
||||
|
||||
func setFunctionBreakpoint(p *proc.Target, t testing.TB, fname string) *proc.Breakpoint {
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
f = filepath.Base(f)
|
||||
|
||||
t.Helper()
|
||||
addrs, err := proc.FindFunctionLocation(p, fname, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
|
||||
t.Fatalf("FindFunctionLocation(%s): %v", fname, err)
|
||||
}
|
||||
if len(addrs) != 1 {
|
||||
t.Fatalf("%s:%d: setFunctionBreakpoint(%s): too many results %v", f, l, fname, addrs)
|
||||
t.Fatalf("setFunctionBreakpoint(%s): too many results %v", fname, addrs)
|
||||
}
|
||||
bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
|
||||
t.Fatalf("FindFunctionLocation(%s): %v", fname, err)
|
||||
}
|
||||
return bp
|
||||
}
|
||||
|
||||
func setFunctionBreakpointAll(p *proc.Target, t testing.TB, fname string) {
|
||||
t.Helper()
|
||||
addrs, err := proc.FindFunctionLocation(p, fname, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("FindFunctionLocation(%s): %v", fname, err)
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
_, err := p.SetBreakpoint(int(addr), addr, proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("FindFunctionLocation(%s): %v", fname, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setFileBreakpoint(p *proc.Target, t testing.TB, path string, lineno int) *proc.Breakpoint {
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
f = filepath.Base(f)
|
||||
@ -1927,7 +1939,7 @@ func TestIssue332_Part2(t *testing.T) {
|
||||
assertNoError(err, t, "Registers()")
|
||||
pc := regs.PC()
|
||||
pcAfterPrologue := findFunctionLocation(p, t, "main.changeMe")
|
||||
if pcAfterPrologue == p.BinInfo().LookupFunc["main.changeMe"].Entry {
|
||||
if pcAfterPrologue == p.BinInfo().LookupFunc()["main.changeMe"][0].Entry {
|
||||
t.Fatalf("main.changeMe and main.changeMe:0 are the same (%x)", pcAfterPrologue)
|
||||
}
|
||||
if pc != pcAfterPrologue {
|
||||
@ -2252,7 +2264,7 @@ func TestIssue573(t *testing.T) {
|
||||
|
||||
func TestTestvariables2Prologue(t *testing.T) {
|
||||
withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
addrEntry := p.BinInfo().LookupFunc["main.main"].Entry
|
||||
addrEntry := p.BinInfo().LookupFunc()["main.main"][0].Entry
|
||||
addrPrologue := findFunctionLocation(p, t, "main.main")
|
||||
if addrEntry == addrPrologue {
|
||||
t.Fatalf("Prologue detection failed on testvariables2.go/main.main")
|
||||
@ -3483,7 +3495,7 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) {
|
||||
assertNoError(err, t, "GetG")
|
||||
mainGoroutineID := g.ID
|
||||
|
||||
setFunctionBreakpoint(p, t, "runtime.newstack")
|
||||
setFunctionBreakpointAll(p, t, "runtime.newstack")
|
||||
for {
|
||||
assertNoError(grp.Continue(), t, "second continue")
|
||||
g, err = proc.GetG(p.CurrentThread())
|
||||
@ -3710,7 +3722,7 @@ func TestDisassembleGlobalVars(t *testing.T) {
|
||||
t.Skip("On 386 linux when pie, symLookup can't look up global variables")
|
||||
}
|
||||
withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
mainfn := p.BinInfo().LookupFunc["main.main"]
|
||||
mainfn := p.BinInfo().LookupFunc()["main.main"][0]
|
||||
regs, _ := p.CurrentThread().Registers()
|
||||
text, err := proc.Disassemble(p.Memory(), regs, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End)
|
||||
assertNoError(err, t, "Disassemble")
|
||||
@ -4140,7 +4152,7 @@ func TestStepOutReturn(t *testing.T) {
|
||||
|
||||
func TestOptimizationCheck(t *testing.T) {
|
||||
withTestProcess("continuetestprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
fn := p.BinInfo().LookupFunc["main.main"]
|
||||
fn := p.BinInfo().LookupFunc()["main.main"][0]
|
||||
if fn.Optimized() {
|
||||
t.Fatalf("main.main is optimized")
|
||||
}
|
||||
@ -4148,7 +4160,7 @@ func TestOptimizationCheck(t *testing.T) {
|
||||
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) {
|
||||
withTestProcessArgs("continuetestprog", t, ".", []string{}, protest.EnableOptimization|protest.EnableInlining, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
fn := p.BinInfo().LookupFunc["main.main"]
|
||||
fn := p.BinInfo().LookupFunc()["main.main"][0]
|
||||
if !fn.Optimized() {
|
||||
t.Fatalf("main.main is not optimized")
|
||||
}
|
||||
@ -5818,7 +5830,7 @@ func TestCallInjectionFlagCorruption(t *testing.T) {
|
||||
protest.MustSupportFunctionCalls(t, testBackend)
|
||||
|
||||
withTestProcessArgs("badflags", t, ".", []string{"0"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
mainfn := p.BinInfo().LookupFunc["main.main"]
|
||||
mainfn := p.BinInfo().LookupFunc()["main.main"][0]
|
||||
|
||||
// Find JNZ instruction on line :14
|
||||
var addr uint64
|
||||
|
@ -2991,3 +2991,29 @@ func TestClientServer_autoBreakpoints(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestClientServer_breakpointOnFuncWithABIWrapper(t *testing.T) {
|
||||
// Setting a breakpoint on an assembly function that has an ABI
|
||||
// compatibility wrapper should end up setting a breakpoint on the real
|
||||
// function (also setting a breakpoint on the wrapper is fine).
|
||||
// Issue #3296
|
||||
protest.AllowRecording(t)
|
||||
withTestClient2("math", t, func(c service.Client) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "runtime.schedinit"})
|
||||
assertNoError(err, t, "CreateBreakpoin()")
|
||||
t.Log(bp)
|
||||
|
||||
found := false
|
||||
for _, pc := range bp.Addrs {
|
||||
text, err := c.DisassemblePC(api.EvalScope{}, pc, api.IntelFlavour)
|
||||
assertNoError(err, t, fmt.Sprint("DisassemblePC", pc))
|
||||
t.Log("First instruction for", pc, text[0])
|
||||
if strings.HasSuffix(text[0].Loc.File, "runtime/proc.go") {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("breakpoint not set on the runtime/proc.go function")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user