提交 db930498 编写于 作者: A aarzilli 提交者: Alessandro Arzilli

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
上级 6ab6929b
......@@ -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
}
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 = "\\"
func (t *Term) substitutePathRules() [][2]string {
if t.substitutePathRulesCache != nil {
return t.substitutePathRulesCache
}
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 {
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)
}
spr = append(spr, [2]string{r.From, r.To})
}
return path
t.substitutePathRulesCache = spr
return spr
}
func crossPlatformPath(path string) string {
if runtime.GOOS == "windows" {
return strings.ToLower(path)
}
return path
// 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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册