terminal: restore breakpoints set with line offset on restart (#3425)

Change FindLocation so it can return a substitute location expression
and propagate it to pkg/terminal/command.
When breakpoints are set using the syntax :<lineno> or +<lineno>
produce a substitute location expression that doesn't depend on having
a valid scope and can be used to restore the breakpoint.

Fixes #3423
This commit is contained in:
Alessandro Arzilli 2023-07-20 12:29:59 +02:00 committed by GitHub
parent 8023fa956e
commit ca611db449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 68 deletions

@ -20,7 +20,7 @@ const maxFindLocationCandidates = 5
// LocationSpec is an interface that represents a parsed location spec string.
type LocationSpec interface {
// Find returns all locations that match the location spec.
Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error)
Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, string, error)
}
// NormalLocationSpec represents a basic location spec.
@ -269,15 +269,15 @@ func packageMatch(specPkg, symPkg string, packageMap map[string][]string) bool {
// Find will search all functions in the target program and filter them via the
// regex location spec. Only functions matching the regex will be returned.
func (loc *RegexLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
func (loc *RegexLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
if scope == nil {
//TODO(aarzilli): this needs only the list of function we should make it work
return nil, fmt.Errorf("could not determine location (scope is nil)")
return nil, "", fmt.Errorf("could not determine location (scope is nil)")
}
funcs := scope.BinInfo.Functions
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
if err != nil {
return nil, err
return nil, "", err
}
r := make([]api.Location, 0, len(matches))
for i := range matches {
@ -286,39 +286,39 @@ func (loc *RegexLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalS
r = append(r, addressesToLocation(addrs))
}
}
return r, nil
return r, "", nil
}
// Find returns the locations specified via the address location spec.
func (loc *AddrLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
func (loc *AddrLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, 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 nil, "", fmt.Errorf("could not determine current location (scope is nil)")
}
return []api.Location{{PC: uint64(addr)}}, nil
return []api.Location{{PC: uint64(addr)}}, "", nil
}
v, err := scope.EvalExpression(loc.AddrExpr, proc.LoadConfig{FollowPointers: true})
if err != nil {
return nil, err
return nil, "", err
}
if v.Unreadable != nil {
return nil, v.Unreadable
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
return []api.Location{{PC: addr}}, "", nil
case reflect.Func:
fn := scope.BinInfo.PCToFunc(uint64(v.Base))
pc, err := proc.FirstPCAfterPrologue(t, fn, false)
if err != nil {
return nil, err
return nil, "", err
}
return []api.Location{{PC: pc}}, nil
return []api.Location{{PC: pc}}, v.Name, nil
default:
return nil, fmt.Errorf("wrong expression kind: %v", v.Kind)
return nil, "", fmt.Errorf("wrong expression kind: %v", v.Kind)
}
}
@ -371,7 +371,7 @@ func (ale AmbiguousLocationError) Error() string {
// Find will return a list of locations that match the given location spec.
// This matches each other location spec that does not already have its own spec
// implemented (such as regex, or addr).
func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, string, error) {
limit := maxFindLocationCandidates
var candidateFiles []string
for _, sourceFile := range t.BinInfo().Sources {
@ -396,19 +396,19 @@ func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope
if matching := len(candidateFiles) + len(candidateFuncs); matching == 0 {
if scope == nil {
return nil, fmt.Errorf("location %q not found", locStr)
return nil, "", fmt.Errorf("location %q not found", locStr)
}
// if no result was found this locations string could be an
// expression that the user forgot to prefix with '*', try treating it as
// such.
addrSpec := &AddrLocationSpec{AddrExpr: locStr}
locs, err := addrSpec.Find(t, processArgs, scope, locStr, includeNonExecutableLines, nil)
locs, subst, err := addrSpec.Find(t, processArgs, scope, locStr, includeNonExecutableLines, nil)
if err != nil {
return nil, fmt.Errorf("location %q not found", locStr)
return nil, "", fmt.Errorf("location %q not found", locStr)
}
return locs, nil
return locs, subst, nil
} else if matching > 1 {
return nil, AmbiguousLocationError{Location: locStr, CandidatesString: append(candidateFiles, candidateFuncs...)}
return nil, "", AmbiguousLocationError{Location: locStr, CandidatesString: append(candidateFiles, candidateFuncs...)}
}
// len(candidateFiles) + len(candidateFuncs) == 1
@ -417,12 +417,12 @@ func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope
if len(candidateFiles) == 1 {
if loc.LineOffset < 0 {
//lint:ignore ST1005 backwards compatibility
return nil, fmt.Errorf("Malformed breakpoint location, no line offset specified")
return nil, "", fmt.Errorf("Malformed breakpoint location, no line offset specified")
}
addrs, err = proc.FindFileLocation(t, candidateFiles[0], loc.LineOffset)
if includeNonExecutableLines {
if _, isCouldNotFindLine := err.(*proc.ErrCouldNotFindLine); isCouldNotFindLine {
return []api.Location{{File: candidateFiles[0], Line: loc.LineOffset}}, nil
return []api.Location{{File: candidateFiles[0], Line: loc.LineOffset}}, "", nil
}
}
} else { // len(candidateFuncs) == 1
@ -430,9 +430,9 @@ func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope
}
if err != nil {
return nil, err
return nil, "", err
}
return []api.Location{addressesToLocation(addrs)}, nil
return []api.Location{addressesToLocation(addrs)}, "", nil
}
func (loc *NormalLocationSpec) findFuncCandidates(bi *proc.BinaryInfo, limit int) []string {
@ -585,42 +585,48 @@ func addressesToLocation(addrs []uint64) api.Location {
}
// Find returns the location after adding the offset amount to the current line number.
func (loc *OffsetLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
func (loc *OffsetLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
if scope == nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)")
}
if loc.Offset == 0 {
return []api.Location{{PC: scope.PC}}, nil
return nil, "", fmt.Errorf("could not determine current location (scope is nil)")
}
file, line, fn := scope.BinInfo.PCToLine(scope.PC)
if fn == nil {
return nil, fmt.Errorf("could not determine current location")
if loc.Offset == 0 {
subst := ""
if fn != nil {
subst = fmt.Sprintf("%s:%d", file, line)
}
return []api.Location{{PC: scope.PC}}, subst, nil
}
if fn == nil {
return nil, "", fmt.Errorf("could not determine current location")
}
subst := fmt.Sprintf("%s:%d", file, line+loc.Offset)
addrs, err := proc.FindFileLocation(t, file, line+loc.Offset)
if includeNonExecutableLines {
if _, isCouldNotFindLine := err.(*proc.ErrCouldNotFindLine); isCouldNotFindLine {
return []api.Location{{File: file, Line: line + loc.Offset}}, nil
return []api.Location{{File: file, Line: line + loc.Offset}}, subst, nil
}
}
return []api.Location{addressesToLocation(addrs)}, err
return []api.Location{addressesToLocation(addrs)}, subst, err
}
// Find will return the location at the given line in the current file.
func (loc *LineLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
func (loc *LineLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, string, error) {
if scope == nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)")
return nil, "", fmt.Errorf("could not determine current location (scope is nil)")
}
file, _, fn := scope.BinInfo.PCToLine(scope.PC)
if fn == nil {
return nil, fmt.Errorf("could not determine current location")
return nil, "", fmt.Errorf("could not determine current location")
}
subst := fmt.Sprintf("%s:%d", file, loc.Line)
addrs, err := proc.FindFileLocation(t, file, loc.Line)
if includeNonExecutableLines {
if _, isCouldNotFindLine := err.(*proc.ErrCouldNotFindLine); isCouldNotFindLine {
return []api.Location{{File: file, Line: loc.Line}}, nil
return []api.Location{{File: file, Line: loc.Line}}, subst, nil
}
}
return []api.Location{addressesToLocation(addrs)}, err
return []api.Location{addressesToLocation(addrs)}, subst, err
}
func regexFilterFuncs(filter string, allFuncs []proc.Function) ([]string, error) {

@ -1634,7 +1634,7 @@ func clearAll(t *Term, ctx callContext, args string) error {
var locPCs map[uint64]struct{}
if args != "" {
locs, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args, true, t.substitutePathRules())
locs, _, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args, true, t.substitutePathRules())
if err != nil {
return err
}
@ -1790,14 +1790,16 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) ([]
}
requestedBp.Tracepoint = tracepoint
locs, findLocErr := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
locs, substSpec, findLocErr := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
if findLocErr != nil && requestedBp.Name != "" {
requestedBp.Name = ""
spec = argstr
var err2 error
locs, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
var substSpec2 string
locs, substSpec2, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
if err2 == nil {
findLocErr = nil
substSpec = substSpec2
}
}
if findLocErr != nil && shouldAskToSuspendBreakpoint(t) {
@ -1824,6 +1826,9 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) ([]
if findLocErr != nil {
return nil, findLocErr
}
if substSpec != "" {
spec = substSpec
}
created := []*api.Breakpoint{}
for _, loc := range locs {
@ -2431,7 +2436,7 @@ func getLocation(t *Term, ctx callContext, args string, showContext bool) (file
return loc.File, loc.Line, true, nil
default:
locs, err := t.client.FindLocation(ctx.Scope, args, false, t.substitutePathRules())
locs, _, err := t.client.FindLocation(ctx.Scope, args, false, t.substitutePathRules())
if err != nil {
return "", 0, false, err
}
@ -2505,7 +2510,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
switch cmd {
case "":
locs, err := t.client.FindLocation(ctx.Scope, "+0", true, t.substitutePathRules())
locs, _, err := t.client.FindLocation(ctx.Scope, "+0", true, t.substitutePathRules())
if err != nil {
return err
}
@ -2525,7 +2530,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
}
disasm, disasmErr = t.client.DisassembleRange(ctx.Scope, uint64(startpc), uint64(endpc), flavor)
case "-l":
locs, err := t.client.FindLocation(ctx.Scope, rest, true, t.substitutePathRules())
locs, _, err := t.client.FindLocation(ctx.Scope, rest, true, t.substitutePathRules())
if err != nil {
return err
}

@ -1419,3 +1419,29 @@ func TestCreateBreakpointByLocExpr(t *testing.T) {
}
})
}
func TestRestartBreakpoints(t *testing.T) {
// Tests that breakpoints set using just a line number and with a line
// offset are preserved after restart. See issue #3423.
withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
term.MustExec("break main.main")
term.MustExec("continue")
term.MustExec("break 9")
term.MustExec("break +1")
out := term.MustExec("breakpoints")
t.Log("breakpoints before:\n", out)
term.MustExec("restart")
out = term.MustExec("breakpoints")
t.Log("breakpoints after:\n", out)
bps, err := term.client.ListBreakpoints(false)
assertNoError(t, err, "ListBreakpoints")
for _, bp := range bps {
if bp.ID < 0 {
continue
}
if bp.Addr == 0 {
t.Fatalf("breakpoint %d has address 0", bp.ID)
}
}
})
}

@ -143,7 +143,7 @@ type Client interface {
// * *<address> returns the location corresponding to the specified address
// NOTE: this function does not actually set breakpoints.
// If findInstruction is true FindLocation will only return locations that correspond to instructions.
FindLocation(scope api.EvalScope, loc string, findInstruction bool, substitutePathRules [][2]string) ([]api.Location, error)
FindLocation(scope api.EvalScope, loc string, findInstruction bool, substitutePathRules [][2]string) ([]api.Location, string, error)
// DisassembleRange disassemble code between startPC and endPC
DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error)

@ -737,7 +737,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint, locExpr string,
return nil, err
}
setbp.Expr = func(t *proc.Target) []uint64 {
locs, err := loc.Find(t, d.processArgs, nil, locExpr, false, substitutePathRules)
locs, _, err := loc.Find(t, d.processArgs, nil, locExpr, false, substitutePathRules)
if err != nil || len(locs) != 1 {
logflags.DebuggerLogger().Debugf("could not evaluate breakpoint expression %q: %v (number of results %d)", locExpr, err, len(locs))
return nil
@ -1921,17 +1921,17 @@ func (d *Debugger) CurrentPackage() (string, error) {
}
// FindLocation will find the location specified by 'locStr'.
func (d *Debugger) FindLocation(goid int64, frame, deferredCall int, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
func (d *Debugger) FindLocation(goid int64, frame, deferredCall int, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, string, error) {
d.targetMutex.Lock()
defer d.targetMutex.Unlock()
if _, err := d.target.Valid(); err != nil {
return nil, err
return nil, "", err
}
loc, err := locspec.Parse(locStr)
if err != nil {
return nil, err
return nil, "", err
}
return d.findLocation(goid, frame, deferredCall, locStr, loc, includeNonExecutableLines, substitutePathRules)
@ -1949,18 +1949,23 @@ func (d *Debugger) FindLocationSpec(goid int64, frame, deferredCall int, locStr
return nil, err
}
return d.findLocation(goid, frame, deferredCall, locStr, locSpec, includeNonExecutableLines, substitutePathRules)
locs, _, err := d.findLocation(goid, frame, deferredCall, locStr, locSpec, includeNonExecutableLines, substitutePathRules)
return locs, err
}
func (d *Debugger) findLocation(goid int64, frame, deferredCall int, locStr string, locSpec locspec.LocationSpec, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
func (d *Debugger) findLocation(goid int64, frame, deferredCall int, locStr string, locSpec locspec.LocationSpec, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, string, error) {
locations := []api.Location{}
t := proc.ValidTargets{Group: d.target}
subst := ""
for t.Next() {
pid := t.Pid()
s, _ := proc.ConvertEvalScope(t.Target, goid, frame, deferredCall)
locs, err := locSpec.Find(t.Target, d.processArgs, s, locStr, includeNonExecutableLines, substitutePathRules)
locs, s1, err := locSpec.Find(t.Target, d.processArgs, s, locStr, includeNonExecutableLines, substitutePathRules)
if s1 != "" {
subst = s1
}
if err != nil {
return nil, err
return nil, "", err
}
for i := range locs {
if locs[i].PC == 0 {
@ -1977,7 +1982,7 @@ func (d *Debugger) findLocation(goid int64, frame, deferredCall int, locStr stri
}
locations = append(locations, locs...)
}
return locations, nil
return locations, subst, nil
}
// Disassemble code between startPC and endPC.

@ -306,7 +306,7 @@ type FindLocationArgs struct {
func (c *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error {
var err error
*answer, err = c.debugger.FindLocation(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Loc, false, nil)
*answer, _, err = c.debugger.FindLocation(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Loc, false, nil)
return err
}

@ -414,10 +414,10 @@ func (c *RPCClient) AttachedToExistingProcess() bool {
return out.Answer
}
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool, substitutePathRules [][2]string) ([]api.Location, error) {
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool, substitutePathRules [][2]string) ([]api.Location, string, error) {
var out FindLocationOut
err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions, substitutePathRules}, &out)
return out.Locations, err
return out.Locations, out.SubstituteLocExpr, err
}
// DisassembleRange disassembles code between startPC and endPC

@ -705,7 +705,8 @@ type FindLocationIn struct {
}
type FindLocationOut struct {
Locations []api.Location
Locations []api.Location
SubstituteLocExpr string // if this isn't an empty string it should be passed as the location expression for CreateBreakpoint instead of the original location expression
}
// FindLocation returns concrete location information described by a location expression.
@ -723,7 +724,7 @@ type FindLocationOut struct {
// NOTE: this function does not actually set breakpoints.
func (c *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error {
var err error
out.Locations, err = c.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines, arg.SubstitutePathRules)
out.Locations, out.SubstituteLocExpr, err = c.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines, arg.SubstitutePathRules)
return err
}

@ -89,7 +89,7 @@ type locationFinder1 interface {
}
type locationFinder2 interface {
FindLocation(api.EvalScope, string, bool, [][2]string) ([]api.Location, error)
FindLocation(api.EvalScope, string, bool, [][2]string) ([]api.Location, string, error)
}
func findLocationHelper(t *testing.T, c interface{}, loc string, shouldErr bool, count int, checkAddr uint64) []uint64 {
@ -100,7 +100,7 @@ func findLocationHelper(t *testing.T, c interface{}, loc string, shouldErr bool,
case locationFinder1:
locs, err = c.FindLocation(api.EvalScope{GoroutineID: -1}, loc)
case locationFinder2:
locs, err = c.FindLocation(api.EvalScope{GoroutineID: -1}, loc, false, nil)
locs, _, err = c.FindLocation(api.EvalScope{GoroutineID: -1}, loc, false, nil)
default:
t.Errorf("unexpected type %T passed to findLocationHelper", c)
}

@ -997,14 +997,14 @@ func TestClientServer_FindLocations(t *testing.T) {
findLocationHelper(t, c, `*amap["k"]`, false, 1, findLocationHelper(t, c, `amap["k"]`, false, 1, 0)[0])
locsNoSubst, _ := c.FindLocation(api.EvalScope{GoroutineID: -1}, "_fixtures/locationsprog.go:35", false, nil)
locsNoSubst, _, _ := c.FindLocation(api.EvalScope{GoroutineID: -1}, "_fixtures/locationsprog.go:35", false, nil)
sep := "/"
if strings.Contains(locsNoSubst[0].File, "\\") {
sep = "\\"
}
substRules := [][2]string{{strings.Replace(locsNoSubst[0].File, "locationsprog.go", "", 1), strings.Replace(locsNoSubst[0].File, "_fixtures"+sep+"locationsprog.go", "nonexistent", 1)}}
t.Logf("substitute rules: %q -> %q", substRules[0][0], substRules[0][1])
locsSubst, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "nonexistent/locationsprog.go:35", false, substRules)
locsSubst, _, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "nonexistent/locationsprog.go:35", false, substRules)
if err != nil {
t.Fatalf("FindLocation(locationsprog.go:35) with substitute rules: %v", err)
}
@ -1114,7 +1114,7 @@ func TestClientServer_FindLocations(t *testing.T) {
}
func findLocationHelper2(t *testing.T, c service.Client, loc string, checkLoc *api.Location) *api.Location {
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, loc, false, nil)
locs, _, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, loc, false, nil)
if err != nil {
t.Fatalf("FindLocation(%q) -> error %v", loc, err)
}
@ -1360,7 +1360,7 @@ func TestIssue355(t *testing.T) {
assertError(err, t, "ListGoroutines()")
_, err = c.Stacktrace(gid, 10, 0, &normalLoadConfig)
assertError(err, t, "Stacktrace()")
_, err = c.FindLocation(api.EvalScope{GoroutineID: gid}, "+1", false, nil)
_, _, err = c.FindLocation(api.EvalScope{GoroutineID: gid}, "+1", false, nil)
assertError(err, t, "FindLocation()")
_, err = c.DisassemblePC(api.EvalScope{GoroutineID: -1}, 0x40100, api.IntelFlavour)
assertError(err, t, "DisassemblePC()")
@ -1380,7 +1380,7 @@ func TestDisasm(t *testing.T) {
state := <-ch
assertNoError(state.Err, t, "Continue()")
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", false, nil)
locs, _, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", false, nil)
assertNoError(err, t, "FindLocation()")
if len(locs) != 1 {
t.Fatalf("wrong number of locations for main.main: %d", len(locs))
@ -1639,7 +1639,7 @@ func TestTypesCommand(t *testing.T) {
func TestIssue406(t *testing.T) {
protest.AllowRecording(t)
withTestClient2("issue406", t, func(c service.Client) {
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "issue406.go:146", false, nil)
locs, _, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "issue406.go:146", false, nil)
assertNoError(err, t, "FindLocation()")
_, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC})
assertNoError(err, t, "CreateBreakpoint()")
@ -2253,7 +2253,7 @@ func TestUnknownMethodCall(t *testing.T) {
func TestIssue1703(t *testing.T) {
// Calling Disassemble when there is no current goroutine should work.
withTestClient2("testnextprog", t, func(c service.Client) {
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", true, nil)
locs, _, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", true, nil)
assertNoError(err, t, "FindLocation")
t.Logf("FindLocation: %#v", locs)
text, err := c.DisassemblePC(api.EvalScope{GoroutineID: -1}, locs[0].PC, api.IntelFlavour)