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.
This commit is contained in:
Alessandro Arzilli 2019-11-04 17:43:12 +01:00 committed by Derek Parker
parent 222deeec36
commit e8d4ed7ece
5 changed files with 44 additions and 18 deletions

@ -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()
}

@ -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.

@ -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}
}

@ -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
}

@ -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 ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
// * <filename> can be the full path of a file or just a suffix