mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 04:35:19 +08:00
terminal: add -size argument to examinemem command
Adds a -size argument to examinemem that specifies how to group bytes on output.
This commit is contained in:
@ -259,15 +259,15 @@ Aliases: ed
|
||||
## examinemem
|
||||
Examine memory:
|
||||
|
||||
examinemem [-fmt <format>] [-len <length>] <address>
|
||||
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
|
||||
|
||||
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),.
|
||||
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal), addr(address).
|
||||
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.
|
||||
Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
|
||||
|
||||
For example:
|
||||
|
||||
x -fmt hex -len 20 0xc00008af38
|
||||
x -fmt hex -count 20 -size 1 0xc00008af38
|
||||
|
||||
Aliases: x
|
||||
|
||||
|
||||
@ -390,15 +390,15 @@ If locspec is omitted edit will open the current source file in the editor, othe
|
||||
|
||||
{aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine memory:
|
||||
|
||||
examinemem [-fmt <format>] [-len <length>] <address>
|
||||
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
|
||||
|
||||
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),.
|
||||
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal), addr(address).
|
||||
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.
|
||||
Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
|
||||
|
||||
For example:
|
||||
|
||||
x -fmt hex -len 20 0xc00008af38`},
|
||||
x -fmt hex -count 20 -size 1 0xc00008af38`},
|
||||
|
||||
{aliases: []string{"display"}, group: dataCmds, cmdFn: display, helpMsg: `Print value of an expression every time the program stops.
|
||||
|
||||
@ -1562,7 +1562,8 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
||||
|
||||
// Default value
|
||||
priFmt := byte('x')
|
||||
length := 1
|
||||
count := 1
|
||||
size := 1
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
switch v[i] {
|
||||
@ -1585,19 +1586,25 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("%q is not a valid format", v[i])
|
||||
}
|
||||
case "-len":
|
||||
case "-count", "-len":
|
||||
i++
|
||||
if i >= len(v) {
|
||||
return fmt.Errorf("expected argument after -len")
|
||||
return fmt.Errorf("expected argument after -count/-len")
|
||||
}
|
||||
var err error
|
||||
length, err = strconv.Atoi(v[i])
|
||||
if err != nil || length <= 0 {
|
||||
return fmt.Errorf("len must be an positive integer")
|
||||
count, err = strconv.Atoi(v[i])
|
||||
if err != nil || count <= 0 {
|
||||
return fmt.Errorf("count/len must be a positive integer")
|
||||
}
|
||||
// TODO, maybe configured by user.
|
||||
if length > 1000 {
|
||||
return fmt.Errorf("len must be less than or equal to 1000")
|
||||
case "-size":
|
||||
i++
|
||||
if i >= len(v) {
|
||||
return fmt.Errorf("expected argument after -size")
|
||||
}
|
||||
var err error
|
||||
size, err = strconv.Atoi(v[i])
|
||||
if err != nil || size <= 0 || size > 8 {
|
||||
return fmt.Errorf("size must be a positive integer (<=8)")
|
||||
}
|
||||
default:
|
||||
if i != len(v)-1 {
|
||||
@ -1611,16 +1618,20 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO, maybe configured by user.
|
||||
if count*size > 1000 {
|
||||
return fmt.Errorf("read memory range (count*size) must be less than or equal to 1000 bytes")
|
||||
}
|
||||
|
||||
if address == 0 {
|
||||
return fmt.Errorf("no address specified")
|
||||
}
|
||||
|
||||
memArea, err := t.client.ExamineMemory(address, length)
|
||||
memArea, isLittleEndian, err := t.client.ExamineMemory(address, count*size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(api.PrettyExamineMemory(uintptr(address), memArea, priFmt))
|
||||
fmt.Print(api.PrettyExamineMemory(uintptr(address), memArea, isLittleEndian, priFmt, size))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -1093,7 +1093,7 @@ func TestExamineMemoryCmd(t *testing.T) {
|
||||
t.Fatalf("could convert %s into int64, err %s", addressStr, err)
|
||||
}
|
||||
|
||||
res := term.MustExec("examinemem -len 52 -fmt hex " + addressStr)
|
||||
res := term.MustExec("examinemem -count 52 -fmt hex " + addressStr)
|
||||
t.Logf("the result of examining memory \n%s", res)
|
||||
// check first line
|
||||
firstLine := fmt.Sprintf("%#x: 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11", address)
|
||||
@ -1109,7 +1109,7 @@ func TestExamineMemoryCmd(t *testing.T) {
|
||||
|
||||
// second examining memory
|
||||
term.MustExec("continue")
|
||||
res = term.MustExec("x -len 52 -fmt bin " + addressStr)
|
||||
res = term.MustExec("x -count 52 -fmt bin " + addressStr)
|
||||
t.Logf("the second result of examining memory result \n%s", res)
|
||||
|
||||
// check first line
|
||||
|
||||
@ -360,11 +360,17 @@ func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent stri
|
||||
fmt.Fprint(buf, "]")
|
||||
}
|
||||
|
||||
func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
||||
// PrettyExamineMemory examine the memory and format data
|
||||
//
|
||||
// `format` specifies the data format (or data type), `size` specifies size of each data,
|
||||
// like 4byte integer, 1byte character, etc. `count` specifies the number of values.
|
||||
func PrettyExamineMemory(address uintptr, memArea []byte, isLittleEndian bool, format byte, size int) string {
|
||||
|
||||
var (
|
||||
cols int
|
||||
colFormat string
|
||||
colBytes = size
|
||||
|
||||
addrLen int
|
||||
addrFmt string
|
||||
)
|
||||
@ -374,24 +380,24 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
||||
switch format {
|
||||
case 'b':
|
||||
cols = 4 // Avoid emitting rows that are too long when using binary format
|
||||
colFormat = "%08b"
|
||||
colFormat = fmt.Sprintf("%%0%db", colBytes*8)
|
||||
case 'o':
|
||||
cols = 8
|
||||
colFormat = "%04o" // Always keep one leading zero for octal.
|
||||
colFormat = fmt.Sprintf("0%%0%do", colBytes*3) // Always keep one leading zero for octal.
|
||||
case 'd':
|
||||
cols = 8
|
||||
colFormat = "%03d"
|
||||
colFormat = fmt.Sprintf("%%0%dd", colBytes*3)
|
||||
case 'x':
|
||||
cols = 8
|
||||
colFormat = "0x%02x" // Always keep one leading '0x' for hex.
|
||||
colFormat = fmt.Sprintf("0x%%0%dx", colBytes*2) // Always keep one leading '0x' for hex.
|
||||
default:
|
||||
return fmt.Sprintf("not supprted format %q\n", string(format))
|
||||
}
|
||||
colFormat += "\t"
|
||||
|
||||
l := len(memArea)
|
||||
rows := l / cols
|
||||
if l%cols != 0 {
|
||||
rows := l / (cols * colBytes)
|
||||
if l%(cols*colBytes) != 0 {
|
||||
rows++
|
||||
}
|
||||
|
||||
@ -403,10 +409,16 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
||||
|
||||
var b strings.Builder
|
||||
w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
|
||||
|
||||
for i := 0; i < rows; i++ {
|
||||
fmt.Fprintf(w, addrFmt, address)
|
||||
for j := 0; j < cols && i*cols+j < l; j++ {
|
||||
fmt.Fprintf(w, colFormat, memArea[i*cols+j])
|
||||
|
||||
for j := 0; j < cols; j++ {
|
||||
offset := i*(cols*colBytes) + j*colBytes
|
||||
if offset+colBytes <= len(memArea) {
|
||||
n := byteArrayToUInt64(memArea[offset:offset+colBytes], isLittleEndian)
|
||||
fmt.Fprintf(w, colFormat, n)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(w, "")
|
||||
address += uintptr(cols)
|
||||
@ -414,3 +426,17 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
||||
w.Flush()
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func byteArrayToUInt64(buf []byte, isLittleEndian bool) uint64 {
|
||||
var n uint64
|
||||
if isLittleEndian {
|
||||
for i := len(buf) - 1; i >= 0; i-- {
|
||||
n = n<<8 + uint64(buf[i])
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < len(buf); i++ {
|
||||
n = n<<8 + uint64(buf[i])
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -17,7 +18,7 @@ func TestPrettyExamineMemory(t *testing.T) {
|
||||
"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")
|
||||
res := strings.Split(strings.TrimSpace(PrettyExamineMemory(addr, memArea, true, format, 1)), "\n")
|
||||
|
||||
if len(display) != len(res) {
|
||||
t.Fatalf("wrong lines return, expected %d but got %d", len(display), len(res))
|
||||
@ -32,3 +33,27 @@ func TestPrettyExamineMemory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_byteArrayToUInt64(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []byte
|
||||
want uint64
|
||||
}{
|
||||
{"case-nil", nil, 0},
|
||||
{"case-empty", []byte{}, 0},
|
||||
{"case-1", []byte{0x1}, 1},
|
||||
{"case-2", []byte{0x12}, 18},
|
||||
{"case-3", []byte{0x1, 0x2}, 513},
|
||||
{"case-4", []byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2}, 144397766876004609},
|
||||
{"case-5", []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, math.MaxUint64},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := byteArrayToUInt64(tt.args, true)
|
||||
if got != tt.want {
|
||||
t.Errorf("byteArrayToUInt64() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ type Client interface {
|
||||
// ExamineMemory returns the raw memory stored at the given address.
|
||||
// The amount of data to be read is specified by length which must be less than or equal to 1000.
|
||||
// This function will return an error if it reads less than `length` bytes.
|
||||
ExamineMemory(address uint64, length int) ([]byte, error)
|
||||
ExamineMemory(address uint64, length int) ([]byte, bool, error)
|
||||
|
||||
// StopRecording stops a recording if one is in progress.
|
||||
StopRecording() error
|
||||
|
||||
@ -441,15 +441,14 @@ func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
|
||||
return out.List, nil
|
||||
}
|
||||
|
||||
func (c *RPCClient) ExamineMemory(address uint64, count int) ([]byte, error) {
|
||||
func (c *RPCClient) ExamineMemory(address uint64, count int) ([]byte, bool, error) {
|
||||
out := &ExaminedMemoryOut{}
|
||||
|
||||
err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return out.Mem, nil
|
||||
return out.Mem, out.IsLittleEndian, nil
|
||||
}
|
||||
|
||||
func (c *RPCClient) StopRecording() error {
|
||||
|
||||
@ -834,6 +834,7 @@ type ExamineMemoryIn struct {
|
||||
// ExaminedMemoryOut holds the return values of ExamineMemory
|
||||
type ExaminedMemoryOut struct {
|
||||
Mem []byte
|
||||
IsLittleEndian bool
|
||||
}
|
||||
|
||||
func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) error {
|
||||
@ -846,6 +847,8 @@ func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) e
|
||||
}
|
||||
|
||||
out.Mem = Mem
|
||||
out.IsLittleEndian = true //TODO: get byte order from debugger.target.BinInfo().Arch
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user