mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 04:35:19 +08:00
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:
committed by
Alessandro Arzilli
parent
6ab6929b9a
commit
db93049813
@ -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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
||||
@ -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) ([]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.
|
||||
@ -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
|
||||
// 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
|
||||
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||
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.
|
||||
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 {
|
||||
addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64)
|
||||
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.
|
||||
// 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) ([]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
|
||||
var candidateFiles []string
|
||||
for _, file := range scope.BinInfo.Sources {
|
||||
if loc.FileMatch(file) || (len(processArgs) >= 1 && tryMatchRelativePathByProc(loc.Base, processArgs[0], file)) {
|
||||
candidateFiles = append(candidateFiles, file)
|
||||
for _, sourceFile := range scope.BinInfo.Sources {
|
||||
substFile := sourceFile
|
||||
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 {
|
||||
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
|
||||
// such.
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
if len(addrs) <= 0 {
|
||||
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.
|
||||
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 {
|
||||
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.
|
||||
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 {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
|
||||
@ -607,10 +607,10 @@ func threads(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
if th.Function != nil {
|
||||
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())
|
||||
} else {
|
||||
fmt.Printf("%sThread %s\n", prefix, formatThread(th))
|
||||
fmt.Printf("%sThread %s\n", prefix, t.formatThread(th))
|
||||
}
|
||||
}
|
||||
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 {
|
||||
prefix = "* "
|
||||
}
|
||||
fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
|
||||
fmt.Printf("%sGoroutine %s\n", prefix, t.formatGoroutine(g, fgl))
|
||||
if flags&printGoroutinesLabels != 0 {
|
||||
writeGoroutineLabels(os.Stdout, g, "\t")
|
||||
}
|
||||
@ -676,7 +676,7 @@ func printGoroutines(t *Term, gs []*api.Goroutine, fgl formatGoroutineLoc, flags
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printStack(os.Stdout, stack, "\t", false)
|
||||
printStack(t, os.Stdout, stack, "\t", false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -840,7 +840,7 @@ func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, directi
|
||||
}
|
||||
printcontext(t, state)
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
@ -867,18 +867,18 @@ func printscope(t *Term) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Thread %s\n", formatThread(state.CurrentThread))
|
||||
fmt.Printf("Thread %s\n", t.formatThread(state.CurrentThread))
|
||||
if state.SelectedGoroutine != nil {
|
||||
writeGoroutineLong(os.Stdout, state.SelectedGoroutine, "")
|
||||
writeGoroutineLong(t, os.Stdout, state.SelectedGoroutine, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatThread(th *api.Thread) string {
|
||||
func (t *Term) formatThread(th *api.Thread) string {
|
||||
if th == 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
|
||||
@ -890,11 +890,11 @@ const (
|
||||
fglStart
|
||||
)
|
||||
|
||||
func formatLocation(loc api.Location) string {
|
||||
return fmt.Sprintf("%s:%d %s (%#v)", shortenFilePath(loc.File), loc.Line, loc.Function.Name(), loc.PC)
|
||||
func (t *Term) formatLocation(loc api.Location) string {
|
||||
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 {
|
||||
return "<nil>"
|
||||
}
|
||||
@ -921,16 +921,16 @@ func formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string {
|
||||
if g.ThreadID != 0 {
|
||||
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",
|
||||
prefix, g.ID,
|
||||
prefix, formatLocation(g.CurrentLoc),
|
||||
prefix, formatLocation(g.UserCurrentLoc),
|
||||
prefix, formatLocation(g.GoStatementLoc),
|
||||
prefix, formatLocation(g.StartLoc))
|
||||
prefix, t.formatLocation(g.CurrentLoc),
|
||||
prefix, t.formatLocation(g.UserCurrentLoc),
|
||||
prefix, t.formatLocation(g.GoStatementLoc),
|
||||
prefix, t.formatLocation(g.StartLoc))
|
||||
writeGoroutineLabels(w, g, prefix+"\t")
|
||||
}
|
||||
|
||||
@ -1041,7 +1041,7 @@ func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newA
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
@ -1342,7 +1342,7 @@ func clear(t *Term, ctx callContext, args string) error {
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@ -1354,7 +1354,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)
|
||||
locs, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args, true, t.substitutePathRules())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1380,9 +1380,9 @@ func clearAll(t *Term, ctx callContext, args string) error {
|
||||
|
||||
_, err := t.client.ClearBreakpoint(bp.ID)
|
||||
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
|
||||
}
|
||||
@ -1401,7 +1401,7 @@ func breakpoints(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
sort.Sort(byID(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
|
||||
if bp.Cond != "" {
|
||||
@ -1457,7 +1457,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
|
||||
}
|
||||
|
||||
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 requestedBp.Name == "" {
|
||||
return err
|
||||
@ -1465,7 +1465,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
|
||||
requestedBp.Name = ""
|
||||
spec = argstr
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -1482,7 +1482,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) 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
|
||||
@ -1829,7 +1829,7 @@ func stackCommand(t *Term, ctx callContext, args string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printStack(os.Stdout, stack, "", sa.offsets)
|
||||
printStack(t, os.Stdout, stack, "", sa.offsets)
|
||||
if sa.ancestors > 0 {
|
||||
ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
|
||||
if err != nil {
|
||||
@ -1841,7 +1841,7 @@ func stackCommand(t *Term, ctx callContext, args string) error {
|
||||
fmt.Printf("\t%s\n", ancestor.Unreadable)
|
||||
continue
|
||||
}
|
||||
printStack(os.Stdout, ancestor.Stack, "\t", false)
|
||||
printStack(t, os.Stdout, ancestor.Stack, "\t", false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -1970,7 +1970,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)
|
||||
locs, err := t.client.FindLocation(ctx.Scope, args, false, t.substitutePathRules())
|
||||
if err != nil {
|
||||
return "", 0, false, err
|
||||
}
|
||||
@ -2041,7 +2041,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
|
||||
|
||||
switch cmd {
|
||||
case "":
|
||||
locs, err := t.client.FindLocation(ctx.Scope, "+0", true)
|
||||
locs, err := t.client.FindLocation(ctx.Scope, "+0", true, t.substitutePathRules())
|
||||
if err != nil {
|
||||
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)
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -2103,7 +2103,7 @@ func digits(n int) int {
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -2126,7 +2126,7 @@ func printStack(out io.Writer, stack []api.Stackframe, ind string, offsets bool)
|
||||
continue
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
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, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), d.DeferLoc.File, d.DeferLoc.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(), t.formatPath(d.DeferLoc.File), d.DeferLoc.Line)
|
||||
}
|
||||
|
||||
for j := range stack[i].Arguments {
|
||||
@ -2187,7 +2187,7 @@ func printcontext(t *Term, state *api.DebuggerState) {
|
||||
}
|
||||
}
|
||||
if th == nil {
|
||||
printcontextLocation(state.SelectedGoroutine.CurrentLoc)
|
||||
printcontextLocation(t, state.SelectedGoroutine.CurrentLoc)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -2205,8 +2205,8 @@ func printcontext(t *Term, state *api.DebuggerState) {
|
||||
}
|
||||
}
|
||||
|
||||
func printcontextLocation(loc api.Location) {
|
||||
fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), shortenFilePath(loc.File), loc.Line, loc.PC)
|
||||
func printcontextLocation(t *Term, loc api.Location) {
|
||||
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 {
|
||||
fmt.Println(optimizedFunctionWarning)
|
||||
}
|
||||
@ -2227,7 +2227,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
fn := th.Function
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
@ -2258,7 +2258,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
}
|
||||
|
||||
if th.Breakpoint.Tracepoint || th.Breakpoint.TraceReturn {
|
||||
printTracepoint(th, bpname, fn, args, hasReturnValue)
|
||||
printTracepoint(t, th, bpname, fn, args, hasReturnValue)
|
||||
return
|
||||
}
|
||||
|
||||
@ -2267,7 +2267,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
bpname,
|
||||
fn.Name(),
|
||||
args,
|
||||
shortenFilePath(th.File),
|
||||
t.formatPath(th.File),
|
||||
th.Line,
|
||||
th.GoroutineID,
|
||||
hitCount,
|
||||
@ -2278,7 +2278,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
bpname,
|
||||
fn.Name(),
|
||||
args,
|
||||
shortenFilePath(th.File),
|
||||
t.formatPath(th.File),
|
||||
th.Line,
|
||||
th.Breakpoint.TotalHitCount,
|
||||
th.PC)
|
||||
@ -2288,10 +2288,10 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -2313,7 +2313,7 @@ func printBreakpointInfo(th *api.Thread, tracepointOnNewline bool) {
|
||||
|
||||
if bpi.Goroutine != nil {
|
||||
tracepointnl()
|
||||
writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t")
|
||||
writeGoroutineLong(t, os.Stdout, bpi.Goroutine, "\t")
|
||||
}
|
||||
|
||||
for _, v := range bpi.Variables {
|
||||
@ -2340,17 +2340,17 @@ func printBreakpointInfo(th *api.Thread, tracepointOnNewline bool) {
|
||||
if bpi.Stacktrace != nil {
|
||||
tracepointnl()
|
||||
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 {
|
||||
fmt.Fprintf(os.Stderr, "> goroutine(%d): %s%s(%s)", th.GoroutineID, bpname, fn.Name(), args)
|
||||
if !hasReturnValue {
|
||||
fmt.Println()
|
||||
}
|
||||
printBreakpointInfo(th, !hasReturnValue)
|
||||
printBreakpointInfo(t, th, !hasReturnValue)
|
||||
}
|
||||
if th.Breakpoint.TraceReturn {
|
||||
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.BreakpointInfo != nil && th.BreakpointInfo.Stacktrace != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
fh, err := os.Open(name)
|
||||
if err != nil {
|
||||
@ -2625,7 +2618,7 @@ func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
|
||||
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
|
||||
if len(bp.Addrs) > 0 {
|
||||
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, " for ")
|
||||
p := shortenFilePath(bp.File)
|
||||
p := t.formatPath(bp.File)
|
||||
if bp.FunctionName != "" {
|
||||
fmt.Fprintf(&out, "%s() ", bp.FunctionName)
|
||||
}
|
||||
|
||||
@ -247,8 +247,8 @@ func TestExecuteFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue354(t *testing.T) {
|
||||
printStack(os.Stdout, []api.Stackframe{}, "", false)
|
||||
printStack(os.Stdout, []api.Stackframe{
|
||||
printStack(&Term{}, os.Stdout, []api.Stackframe{}, "", false)
|
||||
printStack(&Term{}, os.Stdout, []api.Stackframe{
|
||||
{Location: api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil},
|
||||
Bottom: true}}, "", false)
|
||||
}
|
||||
|
||||
@ -514,6 +514,12 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
||||
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 {
|
||||
var err error
|
||||
switch kv[0].(starlark.String) {
|
||||
@ -523,6 +529,8 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Loc, "Loc")
|
||||
case "IncludeNonExecutableLines":
|
||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.IncludeNonExecutableLines, "IncludeNonExecutableLines")
|
||||
case "SubstitutePathRules":
|
||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.SubstitutePathRules, "SubstitutePathRules")
|
||||
default:
|
||||
err = fmt.Errorf("unknown argument %q", kv[0])
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"net/rpc"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@ -14,6 +13,7 @@ import (
|
||||
"github.com/peterh/liner"
|
||||
|
||||
"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/service"
|
||||
"github.com/go-delve/delve/service/api"
|
||||
@ -60,6 +60,8 @@ type Term struct {
|
||||
|
||||
starlarkEnv *starbind.Env
|
||||
|
||||
substitutePathRulesCache [][2]string
|
||||
|
||||
// quitContinue is set to true by exitCommand to signal that the process
|
||||
// should be resumed before quitting.
|
||||
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
|
||||
// substitution.
|
||||
func (t *Term) substitutePath(path string) string {
|
||||
path = crossPlatformPath(path)
|
||||
if t.conf == nil {
|
||||
return path
|
||||
}
|
||||
|
||||
// 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
|
||||
return locspec.SubstitutePath(path, t.substitutePathRules())
|
||||
}
|
||||
|
||||
func crossPlatformPath(path string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return strings.ToLower(path)
|
||||
func (t *Term) substitutePathRules() [][2]string {
|
||||
if t.substitutePathRulesCache != nil {
|
||||
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) {
|
||||
|
||||
@ -130,7 +130,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) ([]api.Location, error)
|
||||
FindLocation(scope api.EvalScope, loc string, findInstruction bool, substitutePathRules [][2]string) ([]api.Location, error)
|
||||
|
||||
// Disassemble code between startPC and endPC
|
||||
DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error)
|
||||
|
||||
@ -1443,7 +1443,7 @@ func (d *Debugger) CurrentPackage() (string, error) {
|
||||
}
|
||||
|
||||
// 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()
|
||||
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)
|
||||
|
||||
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 {
|
||||
if locs[i].PC == 0 {
|
||||
continue
|
||||
|
||||
@ -304,7 +304,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)
|
||||
*answer, err = c.debugger.FindLocation(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Loc, false, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -356,9 +356,9 @@ func (c *RPCClient) AttachedToExistingProcess() bool {
|
||||
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
|
||||
err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions}, &out)
|
||||
err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions, substitutePathRules}, &out)
|
||||
return out.Locations, err
|
||||
}
|
||||
|
||||
|
||||
@ -600,6 +600,13 @@ type FindLocationIn struct {
|
||||
Scope api.EvalScope
|
||||
Loc string
|
||||
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 {
|
||||
@ -621,7 +628,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)
|
||||
out.Locations, err = c.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines, arg.SubstitutePathRules)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ type locationFinder1 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 {
|
||||
@ -91,7 +91,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)
|
||||
locs, err = c.FindLocation(api.EvalScope{GoroutineID: -1}, loc, false, nil)
|
||||
default:
|
||||
t.Errorf("unexpected type %T passed to findLocationHelper", c)
|
||||
}
|
||||
|
||||
@ -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, `*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) {
|
||||
@ -1022,7 +1039,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)
|
||||
_, 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()")
|
||||
@ -1039,7 +1056,7 @@ func TestDisasm(t *testing.T) {
|
||||
state := <-ch
|
||||
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()")
|
||||
if len(locs) != 1 {
|
||||
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) {
|
||||
protest.AllowRecording(t)
|
||||
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()")
|
||||
_, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
@ -1709,7 +1726,7 @@ func TestAcceptMulticlient(t *testing.T) {
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
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")
|
||||
|
||||
_, 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) {
|
||||
// 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)
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user