Skip to content

Commit

Permalink
miscellaneous improvements to disassembly view
Browse files Browse the repository at this point in the history
- show arguments in a different column
- show unreachable instructions as a lighter color
- highlight jump targets on hover
- jump to jump targets on click
  • Loading branch information
a committed May 11, 2020
1 parent 68b5f4f commit 6a90a67
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 10 deletions.
178 changes: 170 additions & 8 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -1418,17 +1419,33 @@ func updateDisassemblyPanel(container *nucular.Window) {
listp.Label(fmt.Sprintf("TEXT %s(SB) %s", listingPanel.text[0].Loc.Function.Name(), listingPanel.text[0].Loc.File), "LC")
}

oldDisassHoverIdx := listingPanel.disassHoverIdx
listingPanel.disassHoverIdx = -1

reachableColor := style.Text.Color
unreachableColor := style.Text.Color
darken(&unreachableColor)

for gl.Next() {
instr := listingPanel.text[gl.Index()]

if !instr.reach {
style.Text.Color = unreachableColor
} else {
style.Text.Color = reachableColor
}

if instr.Loc.File != lastfile || instr.Loc.Line != lastlineno {
listp.Row(lineheight).Static()
listp.Row(lineheight).Static()
text := ""
if instr.Loc.File == listingPanel.file && instr.Loc.Line-1 < len(listingPanel.listing) && instr.Loc.Line-1 > 0 {
text = strings.TrimSpace(listingPanel.listing[instr.Loc.Line-1].text)
if instr.Loc.File != listingPanel.file || strings.ToLower(filepath.Ext(listingPanel.file)) != ".s" {
listp.Row(lineheight).Static()
listp.Row(lineheight).Static()
text := ""
if instr.Loc.File == listingPanel.file && instr.Loc.Line-1 < len(listingPanel.listing) && instr.Loc.Line-1 > 0 {
text = strings.TrimSpace(listingPanel.listing[instr.Loc.Line-1].text)
}
listp.LayoutFitWidth(listingPanel.id, 1)
listp.Label(fmt.Sprintf("%s:%d: %s", instr.Loc.File, instr.Loc.Line, text), "LC")
}
listp.LayoutFitWidth(listingPanel.id, 1)
listp.Label(fmt.Sprintf("%s:%d: %s", instr.Loc.File, instr.Loc.Line, text), "LC")
lastfile, lastlineno = instr.Loc.File, instr.Loc.Line
}
listp.Row(lineheight).Static()
Expand All @@ -1445,6 +1462,20 @@ func updateDisassemblyPanel(container *nucular.Window) {
cmds.FillRect(rowbounds, 0, style.Selectable.PressedActive.Data.Color)
}

if gl.Index() == listingPanel.disassHoverIdx || gl.Index() == listingPanel.disassHoverClickIdx {
if listingPanel.centerOnDisassHover {
listingPanel.centerOnDisassHover = false
gl.Center()
}
rowbounds := listp.WidgetBounds()
rowbounds.X = listp.Bounds.X
rowbounds.W = listp.Bounds.W
cmds := listp.Commands()
c := style.Selectable.PressedActive.Data.Color
darken(&c)
cmds.FillRect(rowbounds, 0, c)
}

breakpointIcon(listp, instr.Breakpoint, true, "CC", style)

listp.LayoutSetWidth(arroww)
Expand All @@ -1460,11 +1491,142 @@ func updateDisassemblyPanel(container *nucular.Window) {
listp.LayoutFitWidth(listingPanel.id, 10)
listp.Label(fmt.Sprintf("%x", instr.Loc.PC), "LC")
listp.LayoutFitWidth(listingPanel.id, 100)
listp.Label(instr.Text, "LC")
listp.Label(instr.op, "LC")
listp.LayoutFitWidth(listingPanel.id, 100)
listp.Label(instr.args, "LC")

if listp.Input().Mouse.HoveringRect(listp.LastWidgetBounds) {
if instr.dstidx >= 0 {
if listp.Input().Mouse.IsClickInRect(mouse.ButtonLeft, listp.LastWidgetBounds) {
listingPanel.disassHoverClickIdx = instr.dstidx
listingPanel.centerOnDisassHover = true
}
if listingPanel.disassHoverIdx != instr.dstidx {
listingPanel.disassHoverIdx = instr.dstidx
}
}
}

if listingPanel.recenterDisassembly && centerline {
listingPanel.recenterDisassembly = false
gl.Center()
}
}

style.Text.Color = reachableColor

if oldDisassHoverIdx != listingPanel.disassHoverIdx {
listp.Master().Changed()
}
}

type wrappedInstruction struct {
api.AsmInstruction

op string
args string
reach bool
dstidx int
}

var asmprefixes = map[string]bool{
"cs": true,
"ds": true,
"es": true,
"fs": true,
"gs": true,
"ss": true,
"lock": true,
"rep": true,
"repn": true,
"repne": true,
"addr16": true,
"data16": true,
"addr32": true,
"data32": true,
"bnd": true,
"xacquire": true,
"xrelease": true,
"pt": true,
"pn": true,
}

func wrapInstructions(text api.AsmInstructions, curpc uint64) []wrappedInstruction {
r := make([]wrappedInstruction, len(text))
for i := range text {
r[i].AsmInstruction = text[i]

cur := 0
for {
n := strings.Index(text[i].Text[cur:], " ")
if n < 0 {
cur = len(text[i].Text)
break
}
op := strings.ToLower(strings.TrimSpace(text[i].Text[cur : cur+n]))
cur += n + 1
if !asmprefixes[op] && !strings.HasPrefix(op, "rex.") {
break
}
}

r[i].op = strings.TrimSpace(text[i].Text[:cur])
r[i].args = text[i].Text[cur:]
}

marked := make(map[uint64]bool, len(r))
fringe := make(map[uint64]bool, len(r))

fringe[curpc] = true

fringepop := func() uint64 {
for pc := range fringe {
delete(fringe, pc)
return pc
}
return 0
}

fringepush := func(pc uint64) {
if marked[pc] {
return
}
fringe[pc] = true
}

findinstr := func(pc uint64) int {
for i := range r {
if r[i].Loc.PC == pc {
return i
}
}
return -1
}

for len(fringe) > 0 {
pc := fringepop()

for i := findinstr(pc); i >= 0 && i < len(r); i++ {
marked[r[i].Loc.PC] = true
r[i].reach = true
dstpc, err := strconv.ParseUint(r[i].args, 0, 64)
if err == nil {
fringepush(dstpc)
}
if strings.ToLower(r[i].op) == "jmp" {
break
}
}
}

for i := range r {
dstpc, err := strconv.ParseUint(r[i].args, 0, 64)
if err != nil {
r[i].dstidx = -1
continue
}
r[i].dstidx = findinstr(dstpc)
}

return r
}
10 changes: 8 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ var listingPanel struct {
recenterListing bool
recenterDisassembly bool
listing []listline
text api.AsmInstructions
text []wrappedInstruction
framePC uint64
pinnedLoc *api.Location
stale bool
Expand All @@ -180,6 +180,10 @@ var listingPanel struct {

stepIntoInfo stepIntoInfo
stepIntoFilled bool

disassHoverIdx int
disassHoverClickIdx int
centerOnDisassHover bool
}

var wnd nucular.MasterWindow
Expand Down Expand Up @@ -780,6 +784,8 @@ func refreshState(toframe refreshToFrame, clearKind clearKind, state *api.Debugg
func loadDisassembly(p *asyncLoad) {
listingPanel.text = nil
listingPanel.recenterDisassembly = true
listingPanel.disassHoverIdx = -1
listingPanel.disassHoverClickIdx = -1

loc := disassemblyPanel.loc

Expand All @@ -794,7 +800,7 @@ func loadDisassembly(p *asyncLoad) {
return
}

listingPanel.text = text
listingPanel.text = wrapInstructions(text, loc.PC)
listingPanel.framePC = loc.PC
} else {
listingPanel.text = nil
Expand Down
20 changes: 20 additions & 0 deletions misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"testing"

"github.com/aarzilli/gdlv/internal/dlvclient/service/api"
"github.com/aarzilli/gdlv/internal/prettyprint"
)

Expand Down Expand Up @@ -43,3 +44,22 @@ func TestCurrentColumn(t *testing.T) {
c("something\nsomething else\nb", 1)
c("something\nsomething else\nblah", 4)
}

func TestWrapInstruction(t *testing.T) {
c := func(src, tgtop, tgtargs string) {
out := wrapInstructions([]api.AsmInstruction{{Text: src}}, 0)
if out[0].op != tgtop || out[0].args != tgtargs {
t.Errorf("for %q expected op=%q arg=%q got op=%q arg=%q", src, tgtop, tgtargs, out[0].op, out[0].args)
} else {
t.Logf("for %q got op=%q arg=%q", src, out[0].op, out[0].args)
}
}

c("blah", "blah", "")
c("rep blah", "rep blah", "")
c("blah arg1, arg2", "blah", "arg1, arg2")
c("rep blah arg1, arg2", "rep blah", "arg1, arg2")
c("rep lock blah arg1, arg2", "rep lock blah", "arg1, arg2")
c("rex.w blah", "rex.w blah", "")
c("rex.w blah arg1", "rex.w blah", "arg1")
}

0 comments on commit 6a90a67

Please sign in to comment.