terminal: add ability to show disassembly instead of source (#3047)
Adds ability to show current position as disassembly instead of source listing every time execution stops. Change default to show disassembly after step-instruction and source listing in every other case. Fixes #2878
This commit is contained in:
parent
278e4d10c8
commit
1fe03e728c
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user