diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md index 4d73af426e8d6c05f2572ecc14fe3bacc3b438c2..f9eb75bc31c17a368fa4777121e790e2243a3770 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 944d5b79868c5e0cd46126ddb00fc5c249af08e6..ae0df28f265b6ca26009ab61674d52f9a907e959 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -373,10 +373,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() { @@ -1471,7 +1474,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 a547f935d26b929e86e5a4069756ba00877aad2e..384e2c1345ff52e2065ea6097bb0b85fe141ab5d 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 b62e37a8df5f3f3114517645ed546478e83cdd05..99e6381a6ee124acafbd9af08d04fe8931a6a8fd 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 0000000000000000000000000000000000000000..348ed12e7f450371fe0fe1e1b97bb24fd5c47075 --- /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) + } + } +}