service,terminal: apply substitute path to trace/break argument

Change FindLocation to apply substitute path rules to location
expressions. Changes terminal to always print paths after applying
substitutions.

Implements #2203
This commit is contained in:
aarzilli
2020-10-26 13:36:52 +01:00
committed by Alessandro Arzilli
parent 6ab6929b9a
commit db93049813
13 changed files with 174 additions and 116 deletions

View File

@ -30,7 +30,7 @@ detach(Kill) | Equivalent to API call [Detach](https://godoc.org/github.com/go-d
disassemble(Scope, StartPC, EndPC, Flavour) | Equivalent to API call [Disassemble](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Disassemble) disassemble(Scope, StartPC, EndPC, Flavour) | Equivalent to API call [Disassemble](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Disassemble)
eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Eval) eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Eval)
examine_memory(Address, Length) | Equivalent to API call [ExamineMemory](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ExamineMemory) examine_memory(Address, Length) | Equivalent to API call [ExamineMemory](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ExamineMemory)
find_location(Scope, Loc, IncludeNonExecutableLines) | Equivalent to API call [FindLocation](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation) find_location(Scope, Loc, IncludeNonExecutableLines, SubstitutePathRules) | Equivalent to API call [FindLocation](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation)
function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations) function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations)
get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint) get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint)
get_thread(Id) | Equivalent to API call [GetThread](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetThread) get_thread(Id) | Equivalent to API call [GetThread](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetThread)

View File

@ -20,7 +20,7 @@ const maxFindLocationCandidates = 5
// LocationSpec is an interface that represents a parsed location spec string. // LocationSpec is an interface that represents a parsed location spec string.
type LocationSpec interface { type LocationSpec interface {
// Find returns all locations that match the location spec. // Find returns all locations that match the location spec.
Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool) ([]api.Location, error) Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error)
} }
// NormalLocationSpec represents a basic location spec. // NormalLocationSpec represents a basic location spec.
@ -267,7 +267,7 @@ func packageMatch(specPkg, symPkg string, packageMap map[string][]string) bool {
// Find will search all functions in the target program and filter them via the // 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. // 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) ([]api.Location, error) { func (loc *RegexLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
funcs := scope.BinInfo.Functions funcs := scope.BinInfo.Functions
matches, err := regexFilterFuncs(loc.FuncRegex, funcs) matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
if err != nil { if err != nil {
@ -284,7 +284,7 @@ func (loc *RegexLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalS
} }
// Find returns the locations specified via the address location spec. // 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) ([]api.Location, error) { func (loc *AddrLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
if scope == nil { if scope == nil {
addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64) addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64)
if err != nil { if err != nil {
@ -365,12 +365,16 @@ func (ale AmbiguousLocationError) Error() string {
// Find will return a list of locations that match the given location spec. // 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 // This matches each other location spec that does not already have its own spec
// implemented (such as regex, or addr). // implemented (such as regex, or addr).
func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool) ([]api.Location, error) { func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope *proc.EvalScope, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
limit := maxFindLocationCandidates limit := maxFindLocationCandidates
var candidateFiles []string var candidateFiles []string
for _, file := range scope.BinInfo.Sources { for _, sourceFile := range scope.BinInfo.Sources {
if loc.FileMatch(file) || (len(processArgs) >= 1 && tryMatchRelativePathByProc(loc.Base, processArgs[0], file)) { substFile := sourceFile
candidateFiles = append(candidateFiles, file) if len(substitutePathRules) > 0 {
substFile = SubstitutePath(sourceFile, substitutePathRules)
}
if loc.FileMatch(substFile) || (len(processArgs) >= 1 && tryMatchRelativePathByProc(loc.Base, processArgs[0], substFile)) {
candidateFiles = append(candidateFiles, sourceFile)
if len(candidateFiles) >= limit { if len(candidateFiles) >= limit {
break break
} }
@ -402,7 +406,7 @@ func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope
// expression that the user forgot to prefix with '*', try treating it as // expression that the user forgot to prefix with '*', try treating it as
// such. // such.
addrSpec := &AddrLocationSpec{AddrExpr: locStr} addrSpec := &AddrLocationSpec{AddrExpr: locStr}
locs, err := addrSpec.Find(t, processArgs, scope, locStr, includeNonExecutableLines) locs, err := addrSpec.Find(t, processArgs, scope, locStr, includeNonExecutableLines, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("location \"%s\" not found", locStr) return nil, fmt.Errorf("location \"%s\" not found", locStr)
} }
@ -434,6 +438,40 @@ func (loc *NormalLocationSpec) Find(t *proc.Target, processArgs []string, scope
return []api.Location{addressesToLocation(addrs)}, nil return []api.Location{addressesToLocation(addrs)}, nil
} }
func crossPlatformPath(path string) string {
if runtime.GOOS == "windows" {
return strings.ToLower(path)
}
return path
}
// SubstitutePath applies the specified path substitution rules to path.
func SubstitutePath(path string, rules [][2]string) string {
path = crossPlatformPath(path)
// On windows paths returned from headless server are as c:/dir/dir
// though os.PathSeparator is '\\'
separator := "/" //make it default
if strings.Contains(path, "\\") { //dependent on the path
separator = "\\"
}
for _, r := range rules {
from := crossPlatformPath(r[0])
to := r[1]
if !strings.HasSuffix(from, separator) {
from = from + separator
}
if !strings.HasSuffix(to, separator) {
to = to + separator
}
if strings.HasPrefix(path, from) {
return strings.Replace(path, from, to, 1)
}
}
return path
}
func addressesToLocation(addrs []uint64) api.Location { func addressesToLocation(addrs []uint64) api.Location {
if len(addrs) <= 0 { if len(addrs) <= 0 {
return api.Location{} return api.Location{}
@ -442,7 +480,7 @@ func addressesToLocation(addrs []uint64) api.Location {
} }
// Find returns the location after adding the offset amount to the current line number. // 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) ([]api.Location, error) { func (loc *OffsetLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
if scope == nil { 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)")
} }
@ -463,7 +501,7 @@ func (loc *OffsetLocationSpec) Find(t *proc.Target, _ []string, scope *proc.Eval
} }
// Find will return the location at the given line in the current file. // 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) ([]api.Location, error) { func (loc *LineLocationSpec) Find(t *proc.Target, _ []string, scope *proc.EvalScope, _ string, includeNonExecutableLines bool, _ [][2]string) ([]api.Location, error) {
if scope == nil { 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)")
} }

View File

@ -607,10 +607,10 @@ func threads(t *Term, ctx callContext, args string) error {
} }
if th.Function != nil { if th.Function != nil {
fmt.Printf("%sThread %d at %#v %s:%d %s\n", fmt.Printf("%sThread %d at %#v %s:%d %s\n",
prefix, th.ID, th.PC, shortenFilePath(th.File), prefix, th.ID, th.PC, t.formatPath(th.File),
th.Line, th.Function.Name()) th.Line, th.Function.Name())
} else { } else {
fmt.Printf("%sThread %s\n", prefix, formatThread(th)) fmt.Printf("%sThread %s\n", prefix, t.formatThread(th))
} }
} }
return nil return nil
@ -667,7 +667,7 @@ func printGoroutines(t *Term, gs []*api.Goroutine, fgl formatGoroutineLoc, flags
if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID { if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
prefix = "* " prefix = "* "
} }
fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl)) fmt.Printf("%sGoroutine %s\n", prefix, t.formatGoroutine(g, fgl))
if flags&printGoroutinesLabels != 0 { if flags&printGoroutinesLabels != 0 {
writeGoroutineLabels(os.Stdout, g, "\t") writeGoroutineLabels(os.Stdout, g, "\t")
} }
@ -676,7 +676,7 @@ func printGoroutines(t *Term, gs []*api.Goroutine, fgl formatGoroutineLoc, flags
if err != nil { if err != nil {
return err return err
} }
printStack(os.Stdout, stack, "\t", false) printStack(t, os.Stdout, stack, "\t", false)
} }
} }
return nil return nil
@ -840,7 +840,7 @@ func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, directi
} }
printcontext(t, state) printcontext(t, state)
th := stack[frame] th := stack[frame]
fmt.Printf("Frame %d: %s:%d (PC: %x)\n", frame, shortenFilePath(th.File), th.Line, th.PC) fmt.Printf("Frame %d: %s:%d (PC: %x)\n", frame, t.formatPath(th.File), th.Line, th.PC)
printfile(t, th.File, th.Line, true) printfile(t, th.File, th.Line, true)
return nil return nil
} }
@ -867,18 +867,18 @@ func printscope(t *Term) error {
return err return err
} }
fmt.Printf("Thread %s\n", formatThread(state.CurrentThread)) fmt.Printf("Thread %s\n", t.formatThread(state.CurrentThread))
if state.SelectedGoroutine != nil { if state.SelectedGoroutine != nil {
writeGoroutineLong(os.Stdout, state.SelectedGoroutine, "") writeGoroutineLong(t, os.Stdout, state.SelectedGoroutine, "")
} }
return nil return nil
} }
func formatThread(th *api.Thread) string { func (t *Term) formatThread(th *api.Thread) string {
if th == nil { if th == nil {
return "<nil>" return "<nil>"
} }
return fmt.Sprintf("%d at %s:%d", th.ID, shortenFilePath(th.File), th.Line) return fmt.Sprintf("%d at %s:%d", th.ID, t.formatPath(th.File), th.Line)
} }
type formatGoroutineLoc int type formatGoroutineLoc int
@ -890,11 +890,11 @@ const (
fglStart fglStart
) )
func formatLocation(loc api.Location) string { func (t *Term) formatLocation(loc api.Location) string {
return fmt.Sprintf("%s:%d %s (%#v)", shortenFilePath(loc.File), loc.Line, loc.Function.Name(), loc.PC) return fmt.Sprintf("%s:%d %s (%#v)", t.formatPath(loc.File), loc.Line, loc.Function.Name(), loc.PC)
} }
func formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string { func (t *Term) formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string {
if g == nil { if g == nil {
return "<nil>" return "<nil>"
} }
@ -921,16 +921,16 @@ func formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string {
if g.ThreadID != 0 { if g.ThreadID != 0 {
thread = fmt.Sprintf(" (thread %d)", g.ThreadID) thread = fmt.Sprintf(" (thread %d)", g.ThreadID)
} }
return fmt.Sprintf("%d - %s: %s%s", g.ID, locname, formatLocation(loc), thread) return fmt.Sprintf("%d - %s: %s%s", g.ID, locname, t.formatLocation(loc), thread)
} }
func writeGoroutineLong(w io.Writer, g *api.Goroutine, prefix string) { func writeGoroutineLong(t *Term, w io.Writer, g *api.Goroutine, prefix string) {
fmt.Fprintf(w, "%sGoroutine %d:\n%s\tRuntime: %s\n%s\tUser: %s\n%s\tGo: %s\n%s\tStart: %s\n", fmt.Fprintf(w, "%sGoroutine %d:\n%s\tRuntime: %s\n%s\tUser: %s\n%s\tGo: %s\n%s\tStart: %s\n",
prefix, g.ID, prefix, g.ID,
prefix, formatLocation(g.CurrentLoc), prefix, t.formatLocation(g.CurrentLoc),
prefix, formatLocation(g.UserCurrentLoc), prefix, t.formatLocation(g.UserCurrentLoc),
prefix, formatLocation(g.GoStatementLoc), prefix, t.formatLocation(g.GoStatementLoc),
prefix, formatLocation(g.StartLoc)) prefix, t.formatLocation(g.StartLoc))
writeGoroutineLabels(w, g, prefix+"\t") writeGoroutineLabels(w, g, prefix+"\t")
} }
@ -1041,7 +1041,7 @@ func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newA
return err return err
} }
for i := range discarded { for i := range discarded {
fmt.Printf("Discarded %s at %s: %v\n", formatBreakpointName(discarded[i].Breakpoint, false), formatBreakpointLocation(discarded[i].Breakpoint), discarded[i].Reason) fmt.Printf("Discarded %s at %s: %v\n", formatBreakpointName(discarded[i].Breakpoint, false), t.formatBreakpointLocation(discarded[i].Breakpoint), discarded[i].Reason)
} }
return nil return nil
} }
@ -1342,7 +1342,7 @@ func clear(t *Term, ctx callContext, args string) error {
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp)) fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
return nil return nil
} }
@ -1354,7 +1354,7 @@ func clearAll(t *Term, ctx callContext, args string) error {
var locPCs map[uint64]struct{} var locPCs map[uint64]struct{}
if args != "" { if args != "" {
locs, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args, true) locs, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args, true, t.substitutePathRules())
if err != nil { if err != nil {
return err return err
} }
@ -1380,9 +1380,9 @@ func clearAll(t *Term, ctx callContext, args string) error {
_, err := t.client.ClearBreakpoint(bp.ID) _, err := t.client.ClearBreakpoint(bp.ID)
if err != nil { if err != nil {
fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), formatBreakpointLocation(bp), err) fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), t.formatBreakpointLocation(bp), err)
} }
fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp)) fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
} }
return nil return nil
} }
@ -1401,7 +1401,7 @@ func breakpoints(t *Term, ctx callContext, args string) error {
} }
sort.Sort(byID(breakPoints)) sort.Sort(byID(breakPoints))
for _, bp := range breakPoints { for _, bp := range breakPoints {
fmt.Printf("%s at %v (%d)\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp), bp.TotalHitCount) fmt.Printf("%s at %v (%d)\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp), bp.TotalHitCount)
var attrs []string var attrs []string
if bp.Cond != "" { if bp.Cond != "" {
@ -1457,7 +1457,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
} }
requestedBp.Tracepoint = tracepoint requestedBp.Tracepoint = tracepoint
locs, err := t.client.FindLocation(ctx.Scope, spec, true) locs, err := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
if err != nil { if err != nil {
if requestedBp.Name == "" { if requestedBp.Name == "" {
return err return err
@ -1465,7 +1465,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
requestedBp.Name = "" requestedBp.Name = ""
spec = argstr spec = argstr
var err2 error var err2 error
locs, err2 = t.client.FindLocation(ctx.Scope, spec, true) locs, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
if err2 != nil { if err2 != nil {
return err return err
} }
@ -1482,7 +1482,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
return err return err
} }
fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp)) fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
} }
var shouldSetReturnBreakpoints bool var shouldSetReturnBreakpoints bool
@ -1829,7 +1829,7 @@ func stackCommand(t *Term, ctx callContext, args string) error {
if err != nil { if err != nil {
return err return err
} }
printStack(os.Stdout, stack, "", sa.offsets) printStack(t, os.Stdout, stack, "", sa.offsets)
if sa.ancestors > 0 { if sa.ancestors > 0 {
ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth) ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
if err != nil { if err != nil {
@ -1841,7 +1841,7 @@ func stackCommand(t *Term, ctx callContext, args string) error {
fmt.Printf("\t%s\n", ancestor.Unreadable) fmt.Printf("\t%s\n", ancestor.Unreadable)
continue continue
} }
printStack(os.Stdout, ancestor.Stack, "\t", false) printStack(t, os.Stdout, ancestor.Stack, "\t", false)
} }
} }
return nil return nil
@ -1970,7 +1970,7 @@ func getLocation(t *Term, ctx callContext, args string, showContext bool) (file
return loc.File, loc.Line, true, nil return loc.File, loc.Line, true, nil
default: default:
locs, err := t.client.FindLocation(ctx.Scope, args, false) locs, err := t.client.FindLocation(ctx.Scope, args, false, t.substitutePathRules())
if err != nil { if err != nil {
return "", 0, false, err return "", 0, false, err
} }
@ -2041,7 +2041,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
switch cmd { switch cmd {
case "": case "":
locs, err := t.client.FindLocation(ctx.Scope, "+0", true) locs, err := t.client.FindLocation(ctx.Scope, "+0", true, t.substitutePathRules())
if err != nil { if err != nil {
return err return err
} }
@ -2061,7 +2061,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
} }
disasm, disasmErr = t.client.DisassembleRange(ctx.Scope, uint64(startpc), uint64(endpc), flavor) disasm, disasmErr = t.client.DisassembleRange(ctx.Scope, uint64(startpc), uint64(endpc), flavor)
case "-l": case "-l":
locs, err := t.client.FindLocation(ctx.Scope, rest, true) locs, err := t.client.FindLocation(ctx.Scope, rest, true, t.substitutePathRules())
if err != nil { if err != nil {
return err return err
} }
@ -2103,7 +2103,7 @@ func digits(n int) int {
const stacktraceTruncatedMessage = "(truncated)" const stacktraceTruncatedMessage = "(truncated)"
func printStack(out io.Writer, stack []api.Stackframe, ind string, offsets bool) { func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
if len(stack) == 0 { if len(stack) == 0 {
return return
} }
@ -2126,7 +2126,7 @@ func printStack(out io.Writer, stack []api.Stackframe, ind string, offsets bool)
continue continue
} }
fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name()) fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
fmt.Fprintf(out, "%sat %s:%d\n", s, shortenFilePath(stack[i].File), stack[i].Line) fmt.Fprintf(out, "%sat %s:%d\n", s, t.formatPath(stack[i].File), stack[i].Line)
if offsets { if offsets {
fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset) fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
@ -2140,8 +2140,8 @@ func printStack(out io.Writer, stack []api.Stackframe, ind string, offsets bool)
continue continue
} }
fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name()) fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
fmt.Fprintf(out, "%sat %s:%d\n", s2, d.DeferredLoc.File, d.DeferredLoc.Line) fmt.Fprintf(out, "%sat %s:%d\n", s2, t.formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), d.DeferLoc.File, d.DeferLoc.Line) fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), t.formatPath(d.DeferLoc.File), d.DeferLoc.Line)
} }
for j := range stack[i].Arguments { for j := range stack[i].Arguments {
@ -2187,7 +2187,7 @@ func printcontext(t *Term, state *api.DebuggerState) {
} }
} }
if th == nil { if th == nil {
printcontextLocation(state.SelectedGoroutine.CurrentLoc) printcontextLocation(t, state.SelectedGoroutine.CurrentLoc)
return return
} }
} }
@ -2205,8 +2205,8 @@ func printcontext(t *Term, state *api.DebuggerState) {
} }
} }
func printcontextLocation(loc api.Location) { func printcontextLocation(t *Term, loc api.Location) {
fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), shortenFilePath(loc.File), loc.Line, loc.PC) fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), t.formatPath(loc.File), loc.Line, loc.PC)
if loc.Function != nil && loc.Function.Optimized { if loc.Function != nil && loc.Function.Optimized {
fmt.Println(optimizedFunctionWarning) fmt.Println(optimizedFunctionWarning)
} }
@ -2227,7 +2227,7 @@ func printcontextThread(t *Term, th *api.Thread) {
fn := th.Function fn := th.Function
if th.Breakpoint == nil { if th.Breakpoint == nil {
printcontextLocation(api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function}) printcontextLocation(t, api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function})
printReturnValues(th) printReturnValues(th)
return return
} }
@ -2258,7 +2258,7 @@ func printcontextThread(t *Term, th *api.Thread) {
} }
if th.Breakpoint.Tracepoint || th.Breakpoint.TraceReturn { if th.Breakpoint.Tracepoint || th.Breakpoint.TraceReturn {
printTracepoint(th, bpname, fn, args, hasReturnValue) printTracepoint(t, th, bpname, fn, args, hasReturnValue)
return return
} }
@ -2267,7 +2267,7 @@ func printcontextThread(t *Term, th *api.Thread) {
bpname, bpname,
fn.Name(), fn.Name(),
args, args,
shortenFilePath(th.File), t.formatPath(th.File),
th.Line, th.Line,
th.GoroutineID, th.GoroutineID,
hitCount, hitCount,
@ -2278,7 +2278,7 @@ func printcontextThread(t *Term, th *api.Thread) {
bpname, bpname,
fn.Name(), fn.Name(),
args, args,
shortenFilePath(th.File), t.formatPath(th.File),
th.Line, th.Line,
th.Breakpoint.TotalHitCount, th.Breakpoint.TotalHitCount,
th.PC) th.PC)
@ -2288,10 +2288,10 @@ func printcontextThread(t *Term, th *api.Thread) {
} }
printReturnValues(th) printReturnValues(th)
printBreakpointInfo(th, false) printBreakpointInfo(t, th, false)
} }
func printBreakpointInfo(th *api.Thread, tracepointOnNewline bool) { func printBreakpointInfo(t *Term, th *api.Thread, tracepointOnNewline bool) {
if th.BreakpointInfo == nil { if th.BreakpointInfo == nil {
return return
} }
@ -2313,7 +2313,7 @@ func printBreakpointInfo(th *api.Thread, tracepointOnNewline bool) {
if bpi.Goroutine != nil { if bpi.Goroutine != nil {
tracepointnl() tracepointnl()
writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t") writeGoroutineLong(t, os.Stdout, bpi.Goroutine, "\t")
} }
for _, v := range bpi.Variables { for _, v := range bpi.Variables {
@ -2340,17 +2340,17 @@ func printBreakpointInfo(th *api.Thread, tracepointOnNewline bool) {
if bpi.Stacktrace != nil { if bpi.Stacktrace != nil {
tracepointnl() tracepointnl()
fmt.Printf("\tStack:\n") fmt.Printf("\tStack:\n")
printStack(os.Stdout, bpi.Stacktrace, "\t\t", false) printStack(t, os.Stdout, bpi.Stacktrace, "\t\t", false)
} }
} }
func printTracepoint(th *api.Thread, bpname string, fn *api.Function, args string, hasReturnValue bool) { func printTracepoint(t *Term, th *api.Thread, bpname string, fn *api.Function, args string, hasReturnValue bool) {
if th.Breakpoint.Tracepoint { if th.Breakpoint.Tracepoint {
fmt.Fprintf(os.Stderr, "> goroutine(%d): %s%s(%s)", th.GoroutineID, bpname, fn.Name(), args) fmt.Fprintf(os.Stderr, "> goroutine(%d): %s%s(%s)", th.GoroutineID, bpname, fn.Name(), args)
if !hasReturnValue { if !hasReturnValue {
fmt.Println() fmt.Println()
} }
printBreakpointInfo(th, !hasReturnValue) printBreakpointInfo(t, th, !hasReturnValue)
} }
if th.Breakpoint.TraceReturn { if th.Breakpoint.TraceReturn {
retVals := make([]string, 0, len(th.ReturnValues)) retVals := make([]string, 0, len(th.ReturnValues))
@ -2362,7 +2362,7 @@ func printTracepoint(th *api.Thread, bpname string, fn *api.Function, args strin
if th.Breakpoint.TraceReturn || !hasReturnValue { if th.Breakpoint.TraceReturn || !hasReturnValue {
if th.BreakpointInfo != nil && th.BreakpointInfo.Stacktrace != nil { if th.BreakpointInfo != nil && th.BreakpointInfo.Stacktrace != nil {
fmt.Fprintf(os.Stderr, "\tStack:\n") fmt.Fprintf(os.Stderr, "\tStack:\n")
printStack(os.Stderr, th.BreakpointInfo.Stacktrace, "\t\t", false) printStack(t, os.Stderr, th.BreakpointInfo.Stacktrace, "\t\t", false)
} }
} }
} }
@ -2479,13 +2479,6 @@ func conditionCmd(t *Term, ctx callContext, argstr string) error {
return t.client.AmendBreakpoint(bp) return t.client.AmendBreakpoint(bp)
} }
// shortenFilePath take a full file path and attempts to shorten
// it by replacing the current directory to './'.
func shortenFilePath(fullPath string) string {
workingDir, _ := os.Getwd()
return strings.Replace(fullPath, workingDir, ".", 1)
}
func (c *Commands) executeFile(t *Term, name string) error { func (c *Commands) executeFile(t *Term, name string) error {
fh, err := os.Open(name) fh, err := os.Open(name)
if err != nil { if err != nil {
@ -2625,7 +2618,7 @@ func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
return fmt.Sprintf("%s %s", thing, id) return fmt.Sprintf("%s %s", thing, id)
} }
func formatBreakpointLocation(bp *api.Breakpoint) string { func (t *Term) formatBreakpointLocation(bp *api.Breakpoint) string {
var out bytes.Buffer var out bytes.Buffer
if len(bp.Addrs) > 0 { if len(bp.Addrs) > 0 {
for i, addr := range bp.Addrs { for i, addr := range bp.Addrs {
@ -2640,7 +2633,7 @@ func formatBreakpointLocation(bp *api.Breakpoint) string {
fmt.Fprintf(&out, "%#x", bp.Addr) fmt.Fprintf(&out, "%#x", bp.Addr)
} }
fmt.Fprintf(&out, " for ") fmt.Fprintf(&out, " for ")
p := shortenFilePath(bp.File) p := t.formatPath(bp.File)
if bp.FunctionName != "" { if bp.FunctionName != "" {
fmt.Fprintf(&out, "%s() ", bp.FunctionName) fmt.Fprintf(&out, "%s() ", bp.FunctionName)
} }

View File

@ -247,8 +247,8 @@ func TestExecuteFile(t *testing.T) {
} }
func TestIssue354(t *testing.T) { func TestIssue354(t *testing.T) {
printStack(os.Stdout, []api.Stackframe{}, "", false) printStack(&Term{}, os.Stdout, []api.Stackframe{}, "", false)
printStack(os.Stdout, []api.Stackframe{ printStack(&Term{}, os.Stdout, []api.Stackframe{
{Location: api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil}, {Location: api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil},
Bottom: true}}, "", false) Bottom: true}}, "", false)
} }

View File

@ -514,6 +514,12 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
return starlark.None, decorateError(thread, err) return starlark.None, decorateError(thread, err)
} }
} }
if len(args) > 3 && args[3] != starlark.None {
err := unmarshalStarlarkValue(args[3], &rpcArgs.SubstitutePathRules, "SubstitutePathRules")
if err != nil {
return starlark.None, decorateError(thread, err)
}
}
for _, kv := range kwargs { for _, kv := range kwargs {
var err error var err error
switch kv[0].(starlark.String) { switch kv[0].(starlark.String) {
@ -523,6 +529,8 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Loc, "Loc") err = unmarshalStarlarkValue(kv[1], &rpcArgs.Loc, "Loc")
case "IncludeNonExecutableLines": case "IncludeNonExecutableLines":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.IncludeNonExecutableLines, "IncludeNonExecutableLines") err = unmarshalStarlarkValue(kv[1], &rpcArgs.IncludeNonExecutableLines, "IncludeNonExecutableLines")
case "SubstitutePathRules":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.SubstitutePathRules, "SubstitutePathRules")
default: default:
err = fmt.Errorf("unknown argument %q", kv[0]) err = fmt.Errorf("unknown argument %q", kv[0])
} }

View File

@ -6,7 +6,6 @@ import (
"net/rpc" "net/rpc"
"os" "os"
"os/signal" "os/signal"
"runtime"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
@ -14,6 +13,7 @@ import (
"github.com/peterh/liner" "github.com/peterh/liner"
"github.com/go-delve/delve/pkg/config" "github.com/go-delve/delve/pkg/config"
"github.com/go-delve/delve/pkg/locspec"
"github.com/go-delve/delve/pkg/terminal/starbind" "github.com/go-delve/delve/pkg/terminal/starbind"
"github.com/go-delve/delve/service" "github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api" "github.com/go-delve/delve/service/api"
@ -60,6 +60,8 @@ type Term struct {
starlarkEnv *starbind.Env starlarkEnv *starbind.Env
substitutePathRulesCache [][2]string
// quitContinue is set to true by exitCommand to signal that the process // quitContinue is set to true by exitCommand to signal that the process
// should be resumed before quitting. // should be resumed before quitting.
quitContinue bool quitContinue bool
@ -287,40 +289,33 @@ func (t *Term) Println(prefix, str string) {
// in the order they are defined, first rule that matches is used for // in the order they are defined, first rule that matches is used for
// substitution. // substitution.
func (t *Term) substitutePath(path string) string { func (t *Term) substitutePath(path string) string {
path = crossPlatformPath(path)
if t.conf == nil { if t.conf == nil {
return path return path
} }
return locspec.SubstitutePath(path, t.substitutePathRules())
// On windows paths returned from headless server are as c:/dir/dir
// though os.PathSeparator is '\\'
separator := "/" //make it default
if strings.Index(path, "\\") != -1 { //dependent on the path
separator = "\\"
}
for _, r := range t.conf.SubstitutePath {
from := crossPlatformPath(r.From)
to := r.To
if !strings.HasSuffix(from, separator) {
from = from + separator
}
if !strings.HasSuffix(to, separator) {
to = to + separator
}
if strings.HasPrefix(path, from) {
return strings.Replace(path, from, to, 1)
}
}
return path
} }
func crossPlatformPath(path string) string { func (t *Term) substitutePathRules() [][2]string {
if runtime.GOOS == "windows" { if t.substitutePathRulesCache != nil {
return strings.ToLower(path) return t.substitutePathRulesCache
} }
return path if t.conf == nil || t.conf.SubstitutePath == nil {
return nil
}
spr := make([][2]string, 0, len(t.conf.SubstitutePath))
for _, r := range t.conf.SubstitutePath {
spr = append(spr, [2]string{r.From, r.To})
}
t.substitutePathRulesCache = spr
return spr
}
// formatPath applies path substitution rules and shortens the resulting
// path by replacing the current directory with './'
func (t *Term) formatPath(path string) string {
path = t.substitutePath(path)
workingDir, _ := os.Getwd()
return strings.Replace(path, workingDir, ".", 1)
} }
func (t *Term) promptForInput() (string, error) { func (t *Term) promptForInput() (string, error) {

View File

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

View File

@ -1443,7 +1443,7 @@ func (d *Debugger) CurrentPackage() (string, error) {
} }
// FindLocation will find the location specified by 'locStr'. // FindLocation will find the location specified by 'locStr'.
func (d *Debugger) FindLocation(goid, frame, deferredCall int, locStr string, includeNonExecutableLines bool) ([]api.Location, error) { func (d *Debugger) FindLocation(goid, frame, deferredCall int, locStr string, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
d.targetMutex.Lock() d.targetMutex.Lock()
defer d.targetMutex.Unlock() defer d.targetMutex.Unlock()
@ -1458,7 +1458,7 @@ func (d *Debugger) FindLocation(goid, frame, deferredCall int, locStr string, in
s, _ := proc.ConvertEvalScope(d.target, goid, frame, deferredCall) s, _ := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
locs, err := loc.Find(d.target, d.processArgs, s, locStr, includeNonExecutableLines) locs, err := loc.Find(d.target, d.processArgs, s, locStr, includeNonExecutableLines, substitutePathRules)
for i := range locs { for i := range locs {
if locs[i].PC == 0 { if locs[i].PC == 0 {
continue continue

View File

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

View File

@ -356,9 +356,9 @@ func (c *RPCClient) AttachedToExistingProcess() bool {
return out.Answer return out.Answer
} }
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool) ([]api.Location, error) { func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool, substitutePathRules [][2]string) ([]api.Location, error) {
var out FindLocationOut var out FindLocationOut
err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions}, &out) err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions, substitutePathRules}, &out)
return out.Locations, err return out.Locations, err
} }

View File

@ -600,6 +600,13 @@ type FindLocationIn struct {
Scope api.EvalScope Scope api.EvalScope
Loc string Loc string
IncludeNonExecutableLines bool IncludeNonExecutableLines bool
// SubstitutePathRules is a slice of source code path substitution rules,
// the first entry of each pair is the path of a directory as it appears in
// the executable file (i.e. the location of a source file when the program
// was compiled), the second entry of each pair is the location of the same
// directory on the client system.
SubstitutePathRules [][2]string
} }
type FindLocationOut struct { type FindLocationOut struct {
@ -621,7 +628,7 @@ type FindLocationOut struct {
// NOTE: this function does not actually set breakpoints. // NOTE: this function does not actually set breakpoints.
func (c *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error { func (c *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error {
var err error var err error
out.Locations, err = c.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines) out.Locations, err = c.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines, arg.SubstitutePathRules)
return err return err
} }

View File

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

View File

@ -766,6 +766,23 @@ func TestClientServer_FindLocations(t *testing.T) {
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0]) findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0])
findLocationHelper(t, c, `*amap["k"]`, false, 1, findLocationHelper(t, c, `amap["k"]`, false, 1, 0)[0]) 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)
sep := "/"
if strings.Contains(locsNoSubst[0].File, "\\") {
sep = "\\"
}
substRules := [][2]string{[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)
if err != nil {
t.Fatalf("FindLocation(locationsprog.go:35) with substitute rules: %v", err)
}
t.Logf("FindLocation(\"/nonexistent/path/locationsprog.go:35\") -> %#v", locsSubst)
if locsNoSubst[0].PC != locsSubst[0].PC {
t.Fatalf("FindLocation with substitute rules mismatch %#v %#v", locsNoSubst[0], locsSubst[0])
}
}) })
withTestClient2("testnextdefer", t, func(c service.Client) { withTestClient2("testnextdefer", t, func(c service.Client) {
@ -1022,7 +1039,7 @@ func TestIssue355(t *testing.T) {
assertError(err, t, "ListGoroutines()") assertError(err, t, "ListGoroutines()")
_, err = c.Stacktrace(gid, 10, 0, &normalLoadConfig) _, err = c.Stacktrace(gid, 10, 0, &normalLoadConfig)
assertError(err, t, "Stacktrace()") assertError(err, t, "Stacktrace()")
_, err = c.FindLocation(api.EvalScope{GoroutineID: gid}, "+1", false) _, err = c.FindLocation(api.EvalScope{GoroutineID: gid}, "+1", false, nil)
assertError(err, t, "FindLocation()") assertError(err, t, "FindLocation()")
_, err = c.DisassemblePC(api.EvalScope{GoroutineID: -1}, 0x40100, api.IntelFlavour) _, err = c.DisassemblePC(api.EvalScope{GoroutineID: -1}, 0x40100, api.IntelFlavour)
assertError(err, t, "DisassemblePC()") assertError(err, t, "DisassemblePC()")
@ -1039,7 +1056,7 @@ func TestDisasm(t *testing.T) {
state := <-ch state := <-ch
assertNoError(state.Err, t, "Continue()") assertNoError(state.Err, t, "Continue()")
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", false) locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", false, nil)
assertNoError(err, t, "FindLocation()") assertNoError(err, t, "FindLocation()")
if len(locs) != 1 { if len(locs) != 1 {
t.Fatalf("wrong number of locations for main.main: %d", len(locs)) t.Fatalf("wrong number of locations for main.main: %d", len(locs))
@ -1301,7 +1318,7 @@ func TestTypesCommand(t *testing.T) {
func TestIssue406(t *testing.T) { func TestIssue406(t *testing.T) {
protest.AllowRecording(t) protest.AllowRecording(t)
withTestClient2("issue406", t, func(c service.Client) { withTestClient2("issue406", t, func(c service.Client) {
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "issue406.go:146", false) locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "issue406.go:146", false, nil)
assertNoError(err, t, "FindLocation()") assertNoError(err, t, "FindLocation()")
_, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC}) _, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC})
assertNoError(err, t, "CreateBreakpoint()") assertNoError(err, t, "CreateBreakpoint()")
@ -1709,7 +1726,7 @@ func TestAcceptMulticlient(t *testing.T) {
} }
func mustHaveDebugCalls(t *testing.T, c service.Client) { func mustHaveDebugCalls(t *testing.T, c service.Client) {
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "runtime.debugCallV1", false) locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "runtime.debugCallV1", false, nil)
if len(locs) == 0 || err != nil { if len(locs) == 0 || err != nil {
t.Skip("function calls not supported on this version of go") t.Skip("function calls not supported on this version of go")
} }
@ -1753,7 +1770,7 @@ func TestClientServerFunctionCallBadPos(t *testing.T) {
} }
withTestClient2("fncall", t, func(c service.Client) { withTestClient2("fncall", t, func(c service.Client) {
mustHaveDebugCalls(t, c) mustHaveDebugCalls(t, c)
loc, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "fmt/print.go:649", false) loc, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "fmt/print.go:649", false, nil)
assertNoError(err, t, "could not find location") assertNoError(err, t, "could not find location")
_, err = c.CreateBreakpoint(&api.Breakpoint{File: loc[0].File, Line: loc[0].Line}) _, err = c.CreateBreakpoint(&api.Breakpoint{File: loc[0].File, Line: loc[0].Line})
@ -1890,7 +1907,7 @@ func TestUnknownMethodCall(t *testing.T) {
func TestIssue1703(t *testing.T) { func TestIssue1703(t *testing.T) {
// Calling Disassemble when there is no current goroutine should work. // Calling Disassemble when there is no current goroutine should work.
withTestClient2("testnextprog", t, func(c service.Client) { withTestClient2("testnextprog", t, func(c service.Client) {
locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", true) locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main", true, nil)
assertNoError(err, t, "FindLocation") assertNoError(err, t, "FindLocation")
t.Logf("FindLocation: %#v", locs) t.Logf("FindLocation: %#v", locs)
text, err := c.DisassemblePC(api.EvalScope{GoroutineID: -1}, locs[0].PC, api.IntelFlavour) text, err := c.DisassemblePC(api.EvalScope{GoroutineID: -1}, locs[0].PC, api.IntelFlavour)