diff --git a/pkg/config/config.go b/pkg/config/config.go index 6e9622ad..0c0de698 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -9,6 +9,7 @@ import ( "path" "runtime" + "github.com/go-delve/delve/service/api" "gopkg.in/yaml.v2" ) @@ -16,6 +17,10 @@ const ( configDir string = "dlv" configDirHidden string = ".dlv" configFile string = "config.yml" + + PositionSource = "source" + PositionDisassembly = "disassembly" + PositionDefault = "default" ) // SubstitutePathRule describes a rule for substitution of path to source code file. @@ -76,9 +81,19 @@ type Config struct { // called (i.e. when execution stops, listCommand is used, etc) SourceListLineCount *int `yaml:"source-list-line-count,omitempty"` - // DebugFileDirectories is the list of directories Delve will use + // DebugInfoDirectories is the list of directories Delve will use // in order to resolve external debug info files. DebugInfoDirectories []string `yaml:"debug-info-directories"` + + // Position controls how the current position in the program is displayed. + // There are three possible values: + // - source: always show the current position in the program's source + // code. + // - disassembly: always should the current position by disassembling the + // current function. + // - default (or the empty string): use disassembly for step-instruction, + // source for everything else. + Position string `yaml:"position"` } func (c *Config) GetSourceListLineCount() int { @@ -90,6 +105,20 @@ func (c *Config) GetSourceListLineCount() int { return n } +func (c *Config) GetDisassembleFlavour() api.AssemblyFlavour { + if c == nil || c.DisassembleFlavor == nil { + return api.IntelFlavour + } + switch *c.DisassembleFlavor { + case "go": + return api.GoFlavour + case "gnu": + return api.GNUFlavour + default: + return api.IntelFlavour + } +} + // LoadConfig attempts to populate a Config object from the config.yml file. func LoadConfig() (*Config, error) { err := createConfigPath() diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index 64469877..104fae3a 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -1175,7 +1175,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error { return err } printcontext(t, state) - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow) t.onStop() return nil } @@ -1328,7 +1328,7 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error { } printcontext(t, state) } - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow) return nil } @@ -1336,7 +1336,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho defer t.onStop() if !state.NextInProgress { if shouldPrintFile { - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow) } return nil } @@ -1356,7 +1356,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho fallthrough default: t.client.CancelNext() - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow) return err } } else { @@ -1372,7 +1372,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho printcontext(t, state) } if !state.NextInProgress { - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow) return nil } } @@ -1452,7 +1452,7 @@ func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error return err } printcontext(t, state) - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow|printPosStepInstruction) return nil } @@ -2385,17 +2385,7 @@ func disassCommand(t *Term, ctx callContext, args string) error { rest = argv[1] } - flavor := api.IntelFlavour - if t.conf != nil && t.conf.DisassembleFlavor != nil { - switch *t.conf.DisassembleFlavor { - case "go": - flavor = api.GoFlavour - case "gnu": - flavor = api.GNUFlavour - default: - flavor = api.IntelFlavour - } - } + flavor := t.conf.GetDisassembleFlavour() var disasm api.AsmInstructions var disasmErr error @@ -2438,7 +2428,7 @@ func disassCommand(t *Term, ctx callContext, args string) error { return disasmErr } - disasmPrint(disasm, t.stdout) + disasmPrint(disasm, t.stdout, true) return nil } @@ -2678,6 +2668,26 @@ func printTracepoint(t *Term, th *api.Thread, bpname string, fn *api.Function, a } } +type printPosFlags uint8 + +const ( + printPosShowArrow printPosFlags = 1 << iota + printPosStepInstruction +) + +func printPos(t *Term, th *api.Thread, flags printPosFlags) error { + if flags&printPosStepInstruction != 0 { + if t.conf.Position == config.PositionSource { + return printfile(t, th.File, th.Line, flags&printPosShowArrow != 0) + } + return printdisass(t, th.PC) + } + if t.conf.Position == config.PositionDisassembly { + return printdisass(t, th.PC) + } + return printfile(t, th.File, th.Line, flags&printPosShowArrow != 0) +} + func printfile(t *Term, filename string, line int, showArrow bool) error { if filename == "" { return nil @@ -2712,6 +2722,35 @@ func printfile(t *Term, filename string, line int, showArrow bool) error { return t.stdout.ColorizePrint(file.Name(), file, line-lineCount, line+lineCount+1, arrowLine) } +func printdisass(t *Term, pc uint64) error { + disasm, err := t.client.DisassemblePC(api.EvalScope{GoroutineID: -1, Frame: 0, DeferredCall: 0}, pc, t.conf.GetDisassembleFlavour()) + if err != nil { + return err + } + + lineCount := t.conf.GetSourceListLineCount() + + showHeader := true + for i := range disasm { + if disasm[i].AtPC { + s := i - lineCount + if s < 0 { + s = 0 + } + e := i + lineCount + 1 + if e > len(disasm) { + e = len(disasm) + } + showHeader = s == 0 + disasm = disasm[s:e] + break + } + } + + disasmPrint(disasm, t.stdout, showHeader) + return nil +} + // ExitRequestError is returned when the user // exits Delve. type ExitRequestError struct{} @@ -2908,7 +2947,7 @@ func (c *Commands) rewind(t *Term, ctx callContext, args string) error { } printcontext(t, state) } - printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) + printPos(t, state.CurrentThread, printPosShowArrow) return nil } diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go index acc2dbf8..cccf50d3 100644 --- a/pkg/terminal/command_test.go +++ b/pkg/terminal/command_test.go @@ -1331,3 +1331,14 @@ func TestTranscript(t *testing.T) { os.Remove(name) }) } + +func TestDisassPosCmd(t *testing.T) { + withTestTerminal("testvariables2", t, func(term *FakeTerminal) { + term.MustExec("continue") + out := term.MustExec("step-instruction") + t.Logf("%q\n", out) + if !strings.Contains(out, "call $runtime.Breakpoint") && !strings.Contains(out, "CALL runtime.Breakpoint(SB)") { + t.Errorf("output doesn't look like disassembly") + } + }) +} diff --git a/pkg/terminal/disasmprint.go b/pkg/terminal/disasmprint.go index 8e4209b7..1bd53229 100644 --- a/pkg/terminal/disasmprint.go +++ b/pkg/terminal/disasmprint.go @@ -10,10 +10,10 @@ import ( "github.com/go-delve/delve/service/api" ) -func disasmPrint(dv api.AsmInstructions, out io.Writer) { +func disasmPrint(dv api.AsmInstructions, out io.Writer, showHeader bool) { bw := bufio.NewWriter(out) defer bw.Flush() - if len(dv) > 0 && dv[0].Loc.Function != nil { + if len(dv) > 0 && dv[0].Loc.Function != nil && showHeader { fmt.Fprintf(bw, "TEXT %s(SB) %s\n", dv[0].Loc.Function.Name(), dv[0].Loc.File) } tw := tabwriter.NewWriter(bw, 1, 8, 1, '\t', 0)