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"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/service/api"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +17,10 @@ const (
|
|||||||
configDir string = "dlv"
|
configDir string = "dlv"
|
||||||
configDirHidden string = ".dlv"
|
configDirHidden string = ".dlv"
|
||||||
configFile string = "config.yml"
|
configFile string = "config.yml"
|
||||||
|
|
||||||
|
PositionSource = "source"
|
||||||
|
PositionDisassembly = "disassembly"
|
||||||
|
PositionDefault = "default"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SubstitutePathRule describes a rule for substitution of path to source code file.
|
// 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)
|
// called (i.e. when execution stops, listCommand is used, etc)
|
||||||
SourceListLineCount *int `yaml:"source-list-line-count,omitempty"`
|
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.
|
// in order to resolve external debug info files.
|
||||||
DebugInfoDirectories []string `yaml:"debug-info-directories"`
|
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 {
|
func (c *Config) GetSourceListLineCount() int {
|
||||||
@ -90,6 +105,20 @@ func (c *Config) GetSourceListLineCount() int {
|
|||||||
return n
|
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.
|
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
err := createConfigPath()
|
err := createConfigPath()
|
||||||
|
@ -1175,7 +1175,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow)
|
||||||
t.onStop()
|
t.onStop()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1328,7 +1328,7 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
}
|
}
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1336,7 +1336,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
|
|||||||
defer t.onStop()
|
defer t.onStop()
|
||||||
if !state.NextInProgress {
|
if !state.NextInProgress {
|
||||||
if shouldPrintFile {
|
if shouldPrintFile {
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1356,7 +1356,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
|
|||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
t.client.CancelNext()
|
t.client.CancelNext()
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1372,7 +1372,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
|
|||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
}
|
}
|
||||||
if !state.NextInProgress {
|
if !state.NextInProgress {
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1452,7 +1452,7 @@ func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow|printPosStepInstruction)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2385,17 +2385,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
|
|||||||
rest = argv[1]
|
rest = argv[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
flavor := api.IntelFlavour
|
flavor := t.conf.GetDisassembleFlavour()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var disasm api.AsmInstructions
|
var disasm api.AsmInstructions
|
||||||
var disasmErr error
|
var disasmErr error
|
||||||
@ -2438,7 +2428,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
|
|||||||
return disasmErr
|
return disasmErr
|
||||||
}
|
}
|
||||||
|
|
||||||
disasmPrint(disasm, t.stdout)
|
disasmPrint(disasm, t.stdout, true)
|
||||||
|
|
||||||
return nil
|
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 {
|
func printfile(t *Term, filename string, line int, showArrow bool) error {
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
return nil
|
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)
|
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
|
// ExitRequestError is returned when the user
|
||||||
// exits Delve.
|
// exits Delve.
|
||||||
type ExitRequestError struct{}
|
type ExitRequestError struct{}
|
||||||
@ -2908,7 +2947,7 @@ func (c *Commands) rewind(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
}
|
}
|
||||||
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
printPos(t, state.CurrentThread, printPosShowArrow)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1331,3 +1331,14 @@ func TestTranscript(t *testing.T) {
|
|||||||
os.Remove(name)
|
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"
|
"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)
|
bw := bufio.NewWriter(out)
|
||||||
defer bw.Flush()
|
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)
|
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)
|
tw := tabwriter.NewWriter(bw, 1, 8, 1, '\t', 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user