From e8d4ed7ecef4b1a0e7727e3ba54983d075d4db08 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Mon, 4 Nov 2019 17:43:12 +0100 Subject: [PATCH] service,terminal: support logical breakpoints (#1742) Changes CreateBreakpoint to create a logical breakpoint when multiple addresses are specified, FindLocation and the api.Location type to return logical locations and the cli to support logical breakpoints. --- pkg/terminal/command.go | 24 ++++++++++++++++++++++-- service/api/types.go | 5 +++++ service/debugger/debugger.go | 2 ++ service/debugger/locations.go | 26 +++++++++++--------------- service/rpc2/server.go | 5 ++++- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index ea8f7d8f..e918a4e3 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -4,6 +4,7 @@ package terminal import ( "bufio" + "bytes" "errors" "fmt" "go/parser" @@ -1188,6 +1189,9 @@ func clearAll(t *Term, ctx callContext, args string) error { } locPCs = make(map[uint64]struct{}) for _, loc := range locs { + for _, pc := range loc.PCs { + locPCs[pc] = struct{}{} + } locPCs[loc.PC] = struct{}{} } } @@ -1297,6 +1301,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err } for _, loc := range locs { requestedBp.Addr = loc.PC + requestedBp.Addrs = loc.PCs bp, err := t.client.CreateBreakpoint(requestedBp) if err != nil { @@ -2222,9 +2227,24 @@ func formatBreakpointName(bp *api.Breakpoint, upcase bool) string { } func formatBreakpointLocation(bp *api.Breakpoint) string { + var out bytes.Buffer + if len(bp.Addrs) > 0 { + for i, addr := range bp.Addrs { + if i == 0 { + fmt.Fprintf(&out, "%#x", addr) + } else { + fmt.Fprintf(&out, ",%#x", addr) + } + } + } else { + // In case we are connecting to an older version of delve that does not return the Addrs field. + fmt.Fprintf(&out, "%#x", bp.Addr) + } + fmt.Fprintf(&out, " for ") p := ShortenFilePath(bp.File) if bp.FunctionName != "" { - return fmt.Sprintf("%#v for %s() %s:%d", bp.Addr, bp.FunctionName, p, bp.Line) + fmt.Fprintf(&out, "%s() ", bp.FunctionName) } - return fmt.Sprintf("%#v for %s:%d", bp.Addr, p, bp.Line) + fmt.Fprintf(&out, "%s:%d", p, bp.Line) + return out.String() } diff --git a/service/api/types.go b/service/api/types.go index 5593801c..33e0dffa 100644 --- a/service/api/types.go +++ b/service/api/types.go @@ -126,11 +126,16 @@ type Thread struct { } // Location holds program location information. +// In most cases a Location object will represent a physical location, with +// a single PC address held in the PC field. +// FindLocations however returns logical locations that can either have +// multiple PC addresses each (due to inlining) or no PC address at all. type Location struct { PC uint64 `json:"pc"` File string `json:"file"` Line int `json:"line"` Function *Function `json:"function,omitempty"` + PCs []uint64 `json:"pcs,omitempty"` } // Stackframe describes one frame in a stack trace. diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 7295f47b..ff979b25 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -446,6 +446,8 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin addrs, err = proc.FindFileLocation(d.target, fileName, requestedBp.Line) case len(requestedBp.FunctionName) > 0: addrs, err = proc.FindFunctionLocation(d.target, requestedBp.FunctionName, requestedBp.Line) + case len(requestedBp.Addrs) > 0: + addrs = requestedBp.Addrs default: addrs = []uint64{requestedBp.Addr} } diff --git a/service/debugger/locations.go b/service/debugger/locations.go index 456c59bb..16827fdf 100644 --- a/service/debugger/locations.go +++ b/service/debugger/locations.go @@ -251,8 +251,8 @@ func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr st r := make([]api.Location, 0, len(matches)) for i := range matches { addrs, _ := proc.FindFunctionLocation(d.target, matches[i], 0) - for _, addr := range addrs { - r = append(r, api.Location{PC: addr}) + if len(addrs) > 0 { + r = append(r, addressesToLocation(addrs)) } } return r, nil @@ -283,7 +283,7 @@ func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str if err != nil { return nil, err } - return []api.Location{{PC: uint64(pc)}}, nil + return []api.Location{{PC: pc}}, nil default: return nil, fmt.Errorf("wrong expression kind: %v", v.Kind) } @@ -389,25 +389,21 @@ func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s return []api.Location{{File: candidateFiles[0], Line: loc.LineOffset}}, nil } } - } else { // len(candidateFUncs) == 1 + } else { // len(candidateFuncs) == 1 addrs, err = proc.FindFunctionLocation(d.target, candidateFuncs[0], loc.LineOffset) } if err != nil { return nil, err } - return addressesToLocations(addrs), nil + return []api.Location{addressesToLocation(addrs)}, nil } -func addressesToLocations(addrs []uint64) []api.Location { - if addrs == nil { - return nil +func addressesToLocation(addrs []uint64) api.Location { + if len(addrs) <= 0 { + return api.Location{} } - r := make([]api.Location, len(addrs)) - for i := range addrs { - r[i] = api.Location{PC: addrs[i]} - } - return r + return api.Location{PC: addrs[0], PCs: addrs} } func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool) ([]api.Location, error) { @@ -427,7 +423,7 @@ func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s return []api.Location{{File: file, Line: line + loc.Offset}}, nil } } - return addressesToLocations(addrs), err + return []api.Location{addressesToLocation(addrs)}, err } func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool) ([]api.Location, error) { @@ -444,5 +440,5 @@ func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str return []api.Location{{File: file, Line: loc.Line}}, nil } } - return addressesToLocations(addrs), err + return []api.Location{addressesToLocation(addrs)}, err } diff --git a/service/rpc2/server.go b/service/rpc2/server.go index 4762ec1d..730f1e9c 100644 --- a/service/rpc2/server.go +++ b/service/rpc2/server.go @@ -231,6 +231,9 @@ type CreateBreakpointOut struct { // the breakpoint will be created on the specified function:line // location. // +// - If arg.Breakpoint.Addrs is filled it will create a logical breakpoint +// corresponding to all specified addresses. +// // - Otherwise the value specified by arg.Breakpoint.Addr will be used. func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error { createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint) @@ -577,7 +580,7 @@ type FindLocationOut struct { Locations []api.Location } -// FindLocation returns concrete location information described by a location expression +// FindLocation returns concrete location information described by a location expression. // // loc ::= : | [:] | // | (+|-) | | *
// * can be the full path of a file or just a suffix