diff --git a/_fixtures/locationsprog2.go b/_fixtures/locationsprog2.go new file mode 100644 index 00000000..d497dd5a --- /dev/null +++ b/_fixtures/locationsprog2.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "runtime" +) + +func afunction(s string) { + fmt.Println(s) +} + +type someStruct struct { + s string +} + +func (o *someStruct) structfunc(s2 string) { + fmt.Println(o.s, s2) +} + +func main() { + fn1 := afunction + var o someStruct + fn2 := o.structfunc + fn3 := func(s string) { + fmt.Println("inline", s) + } + runtime.Breakpoint() + fmt.Println(fn1, fn2, fn3, o) +} diff --git a/proc/eval.go b/proc/eval.go index e690f68f..58b91322 100644 --- a/proc/eval.go +++ b/proc/eval.go @@ -282,7 +282,7 @@ func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { if arg.Unreadable != nil { return nil, arg.Unreadable } - if arg.base == 0 { + if arg.Base == 0 { return newConstant(constant.MakeInt64(0), arg.mem), nil } return newConstant(arg.Children[1].Value, arg.mem), nil @@ -315,7 +315,7 @@ func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { if arg.Unreadable != nil { return nil, arg.Unreadable } - if arg.base == 0 { + if arg.Base == 0 { return newConstant(constant.MakeInt64(0), arg.mem), nil } return newConstant(arg.Children[0].Value, arg.mem), nil @@ -507,7 +507,7 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) { switch xev.Kind { case reflect.Slice, reflect.Array, reflect.String: - if xev.base == 0 { + if xev.Base == 0 { return nil, fmt.Errorf("can not index \"%s\"", exprToString(node.X)) } n, err := idxev.asInt() @@ -567,7 +567,7 @@ func (scope *EvalScope) evalReslice(node *ast.SliceExpr) (*Variable, error) { switch xev.Kind { case reflect.Slice, reflect.Array, reflect.String: - if xev.base == 0 { + if xev.Base == 0 { return nil, fmt.Errorf("can not slice \"%s\"", exprToString(node.X)) } return xev.reslice(low, high) @@ -917,7 +917,7 @@ func (v *Variable) isNil() bool { case reflect.Interface: return false case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan: - return v.base == 0 + return v.Base == 0 } return false } @@ -1039,7 +1039,7 @@ func (v *Variable) sliceAccess(idx int) (*Variable, error) { if idx < 0 || int64(idx) >= v.Len { return nil, fmt.Errorf("index out of bounds") } - return v.newVariable("", v.base+uintptr(int64(idx)*v.stride), v.fieldType), nil + return v.newVariable("", v.Base+uintptr(int64(idx)*v.stride), v.fieldType), nil } func (v *Variable) mapAccess(idx *Variable) (*Variable, error) { @@ -1081,7 +1081,7 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) { return nil, fmt.Errorf("index out of bounds") } - base := v.base + uintptr(int64(low)*v.stride) + base := v.Base + uintptr(int64(low)*v.stride) len := high - low if high-low < 0 { @@ -1104,7 +1104,7 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) { r := v.newVariable("", 0, typ) r.Cap = len r.Len = len - r.base = base + r.Base = base r.stride = v.stride r.fieldType = v.fieldType diff --git a/proc/proc.go b/proc/proc.go index 247a2fbc..32bbbdfa 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -179,26 +179,7 @@ func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOf } if firstLine { - filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry) - if filepath.Ext(filename) != ".go" { - return origfn.Entry, nil - } - for { - lineno++ - pc, fn, _ := dbp.goSymTable.LineToPC(filename, lineno) - if fn != nil { - if fn.Name != funcName { - if strings.Contains(fn.Name, funcName) { - continue - } - break - } - if fn.Name == funcName { - return pc, nil - } - } - } - return origfn.Entry, nil + return dbp.FunctionEntryToFirstLine(origfn.Entry) } else if lineOffset > 0 { filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry) breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset) @@ -208,6 +189,34 @@ func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOf return origfn.Entry, nil } +func (dbp *Process) FunctionEntryToFirstLine(entry uint64) (uint64, error) { + filename, lineno, startfn := dbp.goSymTable.PCToLine(entry) + if filepath.Ext(filename) != ".go" { + return entry, nil + } + if startfn == nil { + return entry, nil + } + funcName := startfn.Name + + for { + lineno++ + pc, fn, _ := dbp.goSymTable.LineToPC(filename, lineno) + if fn != nil { + if fn.Name != funcName { + if strings.Contains(fn.Name, funcName) { + continue + } + break + } + if fn.Name == funcName { + return pc, nil + } + } + } + return entry, nil +} + // RequestManualStop sets the `halt` flag and // sends SIGSTOP to all threads. func (dbp *Process) RequestManualStop() error { diff --git a/proc/variables.go b/proc/variables.go index 33b47bd9..1daf30df 100644 --- a/proc/variables.go +++ b/proc/variables.go @@ -49,11 +49,11 @@ type Variable struct { Len int64 Cap int64 - // base address of arrays, base address of the backing array for slices (0 for nil slices) - // base address of the backing byte array for strings + // Base address of arrays, Base address of the backing array for slices (0 for nil slices) + // Base address of the backing byte array for strings // address of the struct backing chan and map variables // address of the function entry point for function variables (0 for nil function pointers) - base uintptr + Base uintptr stride int64 fieldType dwarf.Type @@ -168,7 +168,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process, v.stride = 1 v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} if v.Addr != 0 { - v.base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.arch, v.Addr) + v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.arch, v.Addr) } case t.StructName == "runtime.iface" || t.StructName == "runtime.eface": v.Kind = reflect.Interface @@ -182,7 +182,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process, } case *dwarf.ArrayType: v.Kind = reflect.Array - v.base = v.Addr + v.Base = v.Addr v.Len = t.Count v.Cap = -1 v.fieldType = t.Type @@ -254,7 +254,7 @@ func newConstant(val constant.Value, mem memoryReadWriter) *Variable { var nilVariable = &Variable{ Addr: 0, - base: 0, + Base: 0, Kind: reflect.Ptr, Children: []Variable{{Addr: 0, OnlyAddr: true}}, } @@ -771,7 +771,7 @@ func (v *Variable) loadValue() { } func (v *Variable) loadValueInternal(recurseLevel int) { - if v.Unreadable != nil || v.loaded || (v.Addr == 0 && v.base == 0) { + if v.Unreadable != nil || v.loaded || (v.Addr == 0 && v.Base == 0) { return } @@ -788,14 +788,14 @@ func (v *Variable) loadValueInternal(recurseLevel int) { sv.loadValueInternal(recurseLevel) v.Children = sv.Children v.Len = sv.Len - v.base = sv.Addr + v.Base = sv.Addr case reflect.Map: v.loadMap(recurseLevel) case reflect.String: var val string - val, v.Unreadable = readStringValue(v.mem, v.base, v.Len) + val, v.Unreadable = readStringValue(v.mem, v.Base, v.Len) v.Value = constant.MakeString(val) case reflect.Slice, reflect.Array: @@ -943,7 +943,7 @@ func (v *Variable) loadSliceInfo(t *dwarf.StructType) { var base uint64 base, err = readUintRaw(v.mem, uintptr(int64(v.Addr)+f.ByteOffset), f.Type.Size()) if err == nil { - v.base = uintptr(base) + v.Base = uintptr(base) // Dereference array type to get value type ptrType, ok := f.Type.(*dwarf.PtrType) if !ok { @@ -993,13 +993,13 @@ func (v *Variable) loadArrayValues(recurseLevel int) { } if v.stride < maxArrayStridePrefetch { - v.mem = cacheMemory(v.mem, v.base, int(v.stride*count)) + v.mem = cacheMemory(v.mem, v.Base, int(v.stride*count)) } errcount := 0 for i := int64(0); i < count; i++ { - fieldvar := v.newVariable("", uintptr(int64(v.base)+(i*v.stride)), v.fieldType) + fieldvar := v.newVariable("", uintptr(int64(v.Base)+(i*v.stride)), v.fieldType) fieldvar.loadValueInternal(recurseLevel + 1) if fieldvar.Unreadable != nil { @@ -1160,7 +1160,7 @@ func (v *Variable) readFunctionPtr() { // dereference pointer to find function pc fnaddr := uintptr(binary.LittleEndian.Uint64(val)) if fnaddr == 0 { - v.base = 0 + v.Base = 0 v.Value = constant.MakeString("") return } @@ -1171,10 +1171,10 @@ func (v *Variable) readFunctionPtr() { return } - v.base = uintptr(binary.LittleEndian.Uint64(val)) - fn := v.dbp.goSymTable.PCToFunc(uint64(v.base)) + v.Base = uintptr(binary.LittleEndian.Uint64(val)) + fn := v.dbp.goSymTable.PCToFunc(uint64(v.Base)) if fn == nil { - v.Unreadable = fmt.Errorf("could not find function for %#v", v.base) + v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base) return } @@ -1238,7 +1238,7 @@ type mapIterator struct { // Code derived from go/src/runtime/hashmap.go func (v *Variable) mapIterator() *mapIterator { sv := v.maybeDereference() - v.base = sv.Addr + v.Base = sv.Addr maptype, ok := sv.RealType.(*dwarf.StructType) if !ok { @@ -1253,7 +1253,7 @@ func (v *Variable) mapIterator() *mapIterator { return it } - v.mem = cacheMemory(v.mem, v.base, int(v.RealType.Size())) + v.mem = cacheMemory(v.mem, v.Base, int(v.RealType.Size())) for _, f := range maptype.Field { var err error diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 6978cb54..f13775bc 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -541,12 +541,9 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat return nil, err } - pc, err := d.process.PC() - if err != nil { - return nil, err - } + s, _ := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame) - locs, err := loc.Find(d, pc, locStr) + locs, err := loc.Find(d, s, locStr) for i := range locs { file, line, fn := d.process.PCToLine(locs[i].PC) locs[i].File = file diff --git a/service/debugger/locations.go b/service/debugger/locations.go index 3e3305bb..ab60f7f6 100644 --- a/service/debugger/locations.go +++ b/service/debugger/locations.go @@ -3,17 +3,20 @@ package debugger import ( "debug/gosym" "fmt" + "go/constant" "path/filepath" + "reflect" "strconv" "strings" + "github.com/derekparker/delve/proc" "github.com/derekparker/delve/service/api" ) const maxFindLocationCandidates = 5 type LocationSpec interface { - Find(d *Debugger, pc uint64, locStr string) ([]api.Location, error) + Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) } type NormalLocationSpec struct { @@ -27,7 +30,7 @@ type RegexLocationSpec struct { } type AddrLocationSpec struct { - Addr uint64 + AddrExpr string } type OffsetLocationSpec struct { @@ -80,15 +83,7 @@ func parseLocationSpec(locStr string) (LocationSpec, error) { } case '*': - rest = rest[1:] - addr, err := strconv.ParseInt(rest, 0, 64) - if err != nil { - return nil, malformed(err.Error()) - } - if addr == 0 { - return nil, malformed("can not set breakpoint at address 0x0") - } - return &AddrLocationSpec{uint64(addr)}, nil + return &AddrLocationSpec{rest[1:]}, nil default: return parseLocationSpecDefault(locStr, rest) @@ -232,7 +227,7 @@ func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool { return true } -func (loc *RegexLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]api.Location, error) { +func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { funcs := d.process.Funcs() matches, err := regexFilterFuncs(loc.FuncRegex, funcs) if err != nil { @@ -248,8 +243,35 @@ func (loc *RegexLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]api return r, nil } -func (loc *AddrLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]api.Location, error) { - return []api.Location{{PC: loc.Addr}}, nil +func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { + if scope == nil { + addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64) + if err != nil { + return nil, fmt.Errorf("could not determine current location (scope is nil)") + } + return []api.Location{{PC: uint64(addr)}}, nil + } else { + v, err := scope.EvalExpression(loc.AddrExpr) + if err != nil { + return nil, err + } + if v.Unreadable != nil { + return nil, v.Unreadable + } + switch v.Kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + addr, _ := constant.Uint64Val(v.Value) + return []api.Location{{PC: addr}}, nil + case reflect.Func: + pc, err := d.process.FunctionEntryToFirstLine(uint64(v.Base)) + if err != nil { + return nil, err + } + return []api.Location{{PC: uint64(pc)}}, nil + default: + return nil, fmt.Errorf("wrong expression kind: %v", v.Kind) + } + } } func (loc *NormalLocationSpec) FileMatch(path string) bool { @@ -283,7 +305,7 @@ func (ale AmbiguousLocationError) Error() string { return fmt.Sprintf("Location \"%s\" ambiguous: %s…", ale.Location, strings.Join(candidates, ", ")) } -func (loc *NormalLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]api.Location, error) { +func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { funcs := d.process.Funcs() files := d.process.Sources() @@ -339,8 +361,11 @@ func (loc *NormalLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]ap } } -func (loc *OffsetLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]api.Location, error) { - file, line, fn := d.process.PCToLine(pc) +func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { + if scope == nil { + return nil, fmt.Errorf("could not determine current location (scope is nil)") + } + file, line, fn := d.process.PCToLine(scope.PC) if fn == nil { return nil, fmt.Errorf("could not determine current location") } @@ -348,8 +373,11 @@ func (loc *OffsetLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]ap return []api.Location{{PC: addr}}, err } -func (loc *LineLocationSpec) Find(d *Debugger, pc uint64, locStr string) ([]api.Location, error) { - file, _, fn := d.process.PCToLine(pc) +func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { + if scope == nil { + return nil, fmt.Errorf("could not determine current location (scope is nil)") + } + file, _, fn := d.process.PCToLine(scope.PC) if fn == nil { return nil, fmt.Errorf("could not determine current location") } diff --git a/service/test/integration_test.go b/service/test/integration_test.go index 25b3744a..61fc486d 100644 --- a/service/test/integration_test.go +++ b/service/test/integration_test.go @@ -610,6 +610,18 @@ func TestClientServer_FindLocations(t *testing.T) { }) } +func TestClientServer_FindLocationsAddr(t *testing.T) { + withTestClient("locationsprog2", t, func(c service.Client) { + <-c.Continue() + + afunction := findLocationHelper(t, c, "main.afunction", false, 1, 0)[0] + anonfunc := findLocationHelper(t, c, "locationsprog2.go:25", false, 1, 0)[0] + + findLocationHelper(t, c, "*fn1", false, 1, afunction) + findLocationHelper(t, c, "*fn3", false, 1, anonfunc) + }) +} + func TestClientServer_EvalVariable(t *testing.T) { withTestClient("testvariables", t, func(c service.Client) { state := <-c.Continue()