From 6f4606a9a926b669a1ba1a392015db04b5e491fd Mon Sep 17 00:00:00 2001 From: chainhelen Date: Mon, 24 Feb 2020 15:28:47 +0800 Subject: [PATCH] pkg,service: Optimized the display of `examinemem` command. 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 #1814. --- Documentation/cli/README.md | 7 ++-- pkg/terminal/command.go | 9 +++-- pkg/terminal/command_test.go | 6 ++-- service/api/prettyprint.go | 59 +++++++++++++++++---------------- service/api/prettyprint_test.go | 34 +++++++++++++++++++ 5 files changed, 79 insertions(+), 36 deletions(-) create mode 100644 service/api/prettyprint_test.go diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md index 32da2428a1..c85a7f429b 100644 --- a/Documentation/cli/README.md +++ b/Documentation/cli/README.md @@ -216,10 +216,13 @@ Examine memory: examinemem [-fmt ] [-len ]
-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 diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index ad4ffd5d50..83174b53d7 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -372,10 +372,13 @@ If locspec is omitted edit will open the current source file in the editor, othe examinemem [-fmt ] [-len ]
-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() { @@ -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 } diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go index 2f74d9046d..854934e315 100644 --- a/pkg/terminal/command_test.go +++ b/pkg/terminal/command_test.go @@ -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) } @@ -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) } diff --git a/service/api/prettyprint.go b/service/api/prettyprint.go index b62e37a8df..6a95175400 100644 --- a/service/api/prettyprint.go +++ b/service/api/prettyprint.go @@ -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) @@ -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" diff --git a/service/api/prettyprint_test.go b/service/api/prettyprint_test.go new file mode 100644 index 0000000000..348ed12e7f --- /dev/null +++ b/service/api/prettyprint_test.go @@ -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) + } + } +}