Skip to content

Commit

Permalink
pkg,service: Optimized the display of examinemem command.
Browse files Browse the repository at this point in the history
1. Don't use intelligent '#' in fmt of go because it is not always satisfying
for diffrent version of golang. Always keep one leading zero for octal and
one leading '0x' for hex manually. Then keep alignment for every byte.

2. Always keep addr alignment when the lens of two adjacent address are
different.

Update go-delve#1814.
  • Loading branch information
chainhelen committed Feb 24, 2020
1 parent 44c644c commit c5912c5
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 36 deletions.
7 changes: 5 additions & 2 deletions Documentation/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,13 @@ Examine memory:

examinemem [-fmt <format>] [-len <length>] <address>

Format represents the data format and the value is one of this list (default hex): oct(octal), hex(hexadecimal), dec(decimal), bin(binary).
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),.
Length is the number of bytes (default 1) and must be less than or equal to 1000.
Address is the memory location of the target to examine.
For example: x -fmt hex -len 20 0xc00008af38

For example:

x -fmt hex -len 20 0xc00008af38

Aliases: x

Expand Down
9 changes: 6 additions & 3 deletions pkg/terminal/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,13 @@ If locspec is omitted edit will open the current source file in the editor, othe
examinemem [-fmt <format>] [-len <length>] <address>
Format represents the data format and the value is one of this list (default hex): oct(octal), hex(hexadecimal), dec(decimal), bin(binary).
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),.
Length is the number of bytes (default 1) and must be less than or equal to 1000.
Address is the memory location of the target to examine.
For example: x -fmt hex -len 20 0xc00008af38`},
For example:
x -fmt hex -len 20 0xc00008af38`},
}

if client == nil || client.Recorded() {
Expand Down Expand Up @@ -1428,7 +1431,7 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
return err
}

fmt.Println(api.PrettyExamineMemory(uintptr(address), memArea, priFmt))
fmt.Printf(api.PrettyExamineMemory(uintptr(address), memArea, priFmt))
return nil
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/terminal/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1009,13 +1009,13 @@ func TestExamineMemoryCmd(t *testing.T) {
res := term.MustExec("examinemem -len 52 -fmt hex " + addressStr)
t.Logf("the result of examining memory \n%s", res)
// check first line
firstLine := fmt.Sprintf("%#x: 0xa 0xb 0xc 0xd 0xe 0xf 0x10 0x11", address)
firstLine := fmt.Sprintf("%#x: 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11", address)
if !strings.Contains(res, firstLine) {
t.Fatalf("expected first line: %s", firstLine)
}

// check last line
lastLine := fmt.Sprintf("%#x: 0x3a 0x3b 0x3c 0x0", address+6*8)
lastLine := fmt.Sprintf("%#x: 0x3a 0x3b 0x3c 0x00", address+6*8)
if !strings.Contains(res, lastLine) {
t.Fatalf("expected last line: %s", lastLine)
}
Expand All @@ -1026,7 +1026,7 @@ func TestExamineMemoryCmd(t *testing.T) {
t.Logf("the second result of examining memory result \n%s", res)

// check first line
firstLine = fmt.Sprintf("%#x: 11111111 00001011 00001100 00001101", address)
firstLine = fmt.Sprintf("%#x: 11111111 00001011 00001100 00001101", address)
if !strings.Contains(res, firstLine) {
t.Fatalf("expected first line: %s", firstLine)
}
Expand Down
59 changes: 31 additions & 28 deletions service/api/prettyprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,31 @@ func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent stri
}

func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
cols := 8
// Avoid emitting rows that are too long when using binary format
if format == 'b' {
cols = 4

var (
cols int
colFormat string
addrLen int
addrFmt string
)

// Diffrent versions of golang output differently about '#'.
// See https://ci.appveyor.com/project/derekparker/delve-facy3/builds/30179356.
switch format {
case 'b':
cols = 4 // Avoid emitting rows that are too long when using binary format
colFormat = "%08b"
case 'o':
cols = 8
colFormat = "%04o" // Always keep one leading zero for octal.
case 'd':
cols = 8
colFormat = "%03d"
case 'x':
cols = 8
colFormat = "0x%02x" // Always keep one leading '0x' for hex.
default:
return fmt.Sprintf("not supprted format %q\n", string(format))
}

l := len(memArea)
Expand All @@ -369,35 +390,17 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
rows++
}

var colFormat string
// Leading zero and occupy 8 for binary
if format == 'b' {
colFormat = " %#08b"
} else {
var maxColCharNum int
for i := 0; i < rows; i++ {
for j := 0; j < cols && i*cols+j < l; j++ {
curColCharNum := len(fmt.Sprintf("%#"+string(format), memArea[i*cols+j]))
if curColCharNum > maxColCharNum {
maxColCharNum = curColCharNum
}
}
}
colFormat = " %#-" + strconv.Itoa(maxColCharNum) + string(format)
// Avoid the lens of two adjacent address are different, so always use the last addr's len to format.
if l != 0 {
addrLen = len(fmt.Sprintf("%x", uint64(address)+uint64(l)))
}
addrFmt = "0x%0" + strconv.Itoa(addrLen) + "x:"

lines := ""
for i := 0; i < rows; i++ {
lines += fmt.Sprintf("%#x:", address)
lines += fmt.Sprintf(addrFmt, address)
for j := 0; j < cols && i*cols+j < l; j++ {
curOutput := fmt.Sprintf(colFormat, memArea[i*cols+j])

// Diffrent versions of golang output differently if binary.
// See https://ci.appveyor.com/project/derekparker/delve-facy3/builds/30179356.
// Remove prefix `0b` if binary in some versions of golang because it is not graceful.
if format == 'b' && strings.Contains(curOutput, "0b") {
curOutput = " " + curOutput[6:]
}
curOutput := " " + fmt.Sprintf(colFormat, memArea[i*cols+j])
lines += curOutput
}
lines += "\n"
Expand Down
34 changes: 34 additions & 0 deletions service/api/prettyprint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package api

import (
"fmt"
"strings"
"testing"
)

func TestPrettyExamineMemory(t *testing.T) {
// Test whether always use the last addr's len to format when the lens of two adjacent address are different
addr := uintptr(0xffff)
memArea := []byte("abcdefghijklmnopqrstuvwxyz")
format := byte('o')

display := []string{
"0x0ffff: 0141 0142 0143 0144 0145 0146 0147 0150",
"0x10007: 0151 0152 0153 0154 0155 0156 0157 0160",
"0x1000f: 0161 0162 0163 0164 0165 0166 0167 0170",
"0x10017: 0171 0172"}
res := strings.Split(strings.TrimSpace(PrettyExamineMemory(addr, memArea, format)), "\n")

if len(display) != len(res) {
t.Fatalf("wrong lines return, expected %d but got %d", len(display), len(res))
}

for i := 0; i < len(display); i++ {
if display[i] != res[i] {
errInfo := fmt.Sprintf("wrong display return at line %d\n", i+1)
errInfo += fmt.Sprintf("expected:\n %q\n", display[i])
errInfo += fmt.Sprintf("but got:\n %q\n", res[i])
t.Fatal(errInfo)
}
}
}

0 comments on commit c5912c5

Please sign in to comment.