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 ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"fmt" "fmt"
"go/parser" "go/parser"
@ -1188,6 +1189,9 @@ func clearAll(t *Term, ctx callContext, args string) error {
} }
locPCs = make(map[uint64]struct{}) locPCs = make(map[uint64]struct{})
for _, loc := range locs { for _, loc := range locs {
for _, pc := range loc.PCs {
locPCs[pc] = struct{}{}
}
locPCs[loc.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 { for _, loc := range locs {
requestedBp.Addr = loc.PC requestedBp.Addr = loc.PC
requestedBp.Addrs = loc.PCs
bp, err := t.client.CreateBreakpoint(requestedBp) bp, err := t.client.CreateBreakpoint(requestedBp)
if err != nil { if err != nil {
@ -2222,9 +2227,24 @@ func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
} }
func formatBreakpointLocation(bp *api.Breakpoint) 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) p := ShortenFilePath(bp.File)
if bp.FunctionName != "" { 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. // 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 { type Location struct {
PC uint64 `json:"pc"` PC uint64 `json:"pc"`
File string `json:"file"` File string `json:"file"`
Line int `json:"line"` Line int `json:"line"`
Function *Function `json:"function,omitempty"` Function *Function `json:"function,omitempty"`
PCs []uint64 `json:"pcs,omitempty"`
} }
// Stackframe describes one frame in a stack trace. // 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) addrs, err = proc.FindFileLocation(d.target, fileName, requestedBp.Line)
case len(requestedBp.FunctionName) > 0: case len(requestedBp.FunctionName) > 0:
addrs, err = proc.FindFunctionLocation(d.target, requestedBp.FunctionName, requestedBp.Line) addrs, err = proc.FindFunctionLocation(d.target, requestedBp.FunctionName, requestedBp.Line)
case len(requestedBp.Addrs) > 0:
addrs = requestedBp.Addrs
default: default:
addrs = []uint64{requestedBp.Addr} 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)) r := make([]api.Location, 0, len(matches))
for i := range matches { for i := range matches {
addrs, _ := proc.FindFunctionLocation(d.target, matches[i], 0) addrs, _ := proc.FindFunctionLocation(d.target, matches[i], 0)
for _, addr := range addrs { if len(addrs) > 0 {
r = append(r, api.Location{PC: addr}) r = append(r, addressesToLocation(addrs))
} }
} }
return r, nil return r, nil
@ -283,7 +283,7 @@ func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []api.Location{{PC: uint64(pc)}}, nil return []api.Location{{PC: pc}}, nil
default: default:
return nil, fmt.Errorf("wrong expression kind: %v", v.Kind) 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 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) addrs, err = proc.FindFunctionLocation(d.target, candidateFuncs[0], loc.LineOffset)
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
return addressesToLocations(addrs), nil return []api.Location{addressesToLocation(addrs)}, nil
} }
func addressesToLocations(addrs []uint64) []api.Location { func addressesToLocation(addrs []uint64) api.Location {
if addrs == nil { if len(addrs) <= 0 {
return nil return api.Location{}
} }
r := make([]api.Location, len(addrs)) return api.Location{PC: addrs[0], PCs: addrs}
for i := range addrs {
r[i] = api.Location{PC: addrs[i]}
}
return r
} }
func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool) ([]api.Location, error) { 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 []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) { 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 []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 // the breakpoint will be created on the specified function:line
// location. // 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. // - Otherwise the value specified by arg.Breakpoint.Addr will be used.
func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error { func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error {
createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint) createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint)
@ -577,7 +580,7 @@ type FindLocationOut struct {
Locations []api.Location 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> // loc ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
// * <filename> can be the full path of a file or just a suffix // * <filename> can be the full path of a file or just a suffix