mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +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
|
## examinemem
|
||||||
Examine memory:
|
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.
|
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:
|
For example:
|
||||||
|
|
||||||
x -fmt hex -len 20 0xc00008af38
|
x -fmt hex -count 20 -size 1 0xc00008af38
|
||||||
|
|
||||||
Aliases: x
|
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:
|
{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.
|
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:
|
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.
|
{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
|
// Default value
|
||||||
priFmt := byte('x')
|
priFmt := byte('x')
|
||||||
length := 1
|
count := 1
|
||||||
|
size := 1
|
||||||
|
|
||||||
for i := 0; i < len(v); i++ {
|
for i := 0; i < len(v); i++ {
|
||||||
switch v[i] {
|
switch v[i] {
|
||||||
@ -1585,19 +1586,25 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%q is not a valid format", v[i])
|
return fmt.Errorf("%q is not a valid format", v[i])
|
||||||
}
|
}
|
||||||
case "-len":
|
case "-count", "-len":
|
||||||
i++
|
i++
|
||||||
if i >= len(v) {
|
if i >= len(v) {
|
||||||
return fmt.Errorf("expected argument after -len")
|
return fmt.Errorf("expected argument after -count/-len")
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
length, err = strconv.Atoi(v[i])
|
count, err = strconv.Atoi(v[i])
|
||||||
if err != nil || length <= 0 {
|
if err != nil || count <= 0 {
|
||||||
return fmt.Errorf("len must be an positive integer")
|
return fmt.Errorf("count/len must be a positive integer")
|
||||||
}
|
}
|
||||||
// TODO, maybe configured by user.
|
case "-size":
|
||||||
if length > 1000 {
|
i++
|
||||||
return fmt.Errorf("len must be less than or equal to 1000")
|
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:
|
default:
|
||||||
if i != len(v)-1 {
|
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 {
|
if address == 0 {
|
||||||
return fmt.Errorf("no address specified")
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Print(api.PrettyExamineMemory(uintptr(address), memArea, isLittleEndian, priFmt, size))
|
||||||
fmt.Print(api.PrettyExamineMemory(uintptr(address), memArea, priFmt))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1093,7 +1093,7 @@ func TestExamineMemoryCmd(t *testing.T) {
|
|||||||
t.Fatalf("could convert %s into int64, err %s", addressStr, err)
|
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)
|
t.Logf("the result of examining memory \n%s", res)
|
||||||
// check first line
|
// check first line
|
||||||
firstLine := fmt.Sprintf("%#x: 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11", address)
|
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
|
// second examining memory
|
||||||
term.MustExec("continue")
|
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)
|
t.Logf("the second result of examining memory result \n%s", res)
|
||||||
|
|
||||||
// check first line
|
// check first line
|
||||||
|
|||||||
@ -360,13 +360,19 @@ func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent stri
|
|||||||
fmt.Fprint(buf, "]")
|
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 (
|
var (
|
||||||
cols int
|
cols int
|
||||||
colFormat string
|
colFormat string
|
||||||
addrLen int
|
colBytes = size
|
||||||
addrFmt string
|
|
||||||
|
addrLen int
|
||||||
|
addrFmt string
|
||||||
)
|
)
|
||||||
|
|
||||||
// Diffrent versions of golang output differently about '#'.
|
// Diffrent versions of golang output differently about '#'.
|
||||||
@ -374,24 +380,24 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
|||||||
switch format {
|
switch format {
|
||||||
case 'b':
|
case 'b':
|
||||||
cols = 4 // Avoid emitting rows that are too long when using binary format
|
cols = 4 // Avoid emitting rows that are too long when using binary format
|
||||||
colFormat = "%08b"
|
colFormat = fmt.Sprintf("%%0%db", colBytes*8)
|
||||||
case 'o':
|
case 'o':
|
||||||
cols = 8
|
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':
|
case 'd':
|
||||||
cols = 8
|
cols = 8
|
||||||
colFormat = "%03d"
|
colFormat = fmt.Sprintf("%%0%dd", colBytes*3)
|
||||||
case 'x':
|
case 'x':
|
||||||
cols = 8
|
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:
|
default:
|
||||||
return fmt.Sprintf("not supprted format %q\n", string(format))
|
return fmt.Sprintf("not supprted format %q\n", string(format))
|
||||||
}
|
}
|
||||||
colFormat += "\t"
|
colFormat += "\t"
|
||||||
|
|
||||||
l := len(memArea)
|
l := len(memArea)
|
||||||
rows := l / cols
|
rows := l / (cols * colBytes)
|
||||||
if l%cols != 0 {
|
if l%(cols*colBytes) != 0 {
|
||||||
rows++
|
rows++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,10 +409,16 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
|||||||
|
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
|
w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
|
||||||
|
|
||||||
for i := 0; i < rows; i++ {
|
for i := 0; i < rows; i++ {
|
||||||
fmt.Fprintf(w, addrFmt, address)
|
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, "")
|
fmt.Fprintln(w, "")
|
||||||
address += uintptr(cols)
|
address += uintptr(cols)
|
||||||
@ -414,3 +426,17 @@ func PrettyExamineMemory(address uintptr, memArea []byte, format byte) string {
|
|||||||
w.Flush()
|
w.Flush()
|
||||||
return b.String()
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -17,7 +18,7 @@ func TestPrettyExamineMemory(t *testing.T) {
|
|||||||
"0x10007: 0151 0152 0153 0154 0155 0156 0157 0160 ",
|
"0x10007: 0151 0152 0153 0154 0155 0156 0157 0160 ",
|
||||||
"0x1000f: 0161 0162 0163 0164 0165 0166 0167 0170 ",
|
"0x1000f: 0161 0162 0163 0164 0165 0166 0167 0170 ",
|
||||||
"0x10017: 0171 0172"}
|
"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) {
|
if len(display) != len(res) {
|
||||||
t.Fatalf("wrong lines return, expected %d but got %d", 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.
|
// 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.
|
// 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.
|
// 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 stops a recording if one is in progress.
|
||||||
StopRecording() error
|
StopRecording() error
|
||||||
|
|||||||
@ -441,15 +441,14 @@ func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
|
|||||||
return out.List, nil
|
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{}
|
out := &ExaminedMemoryOut{}
|
||||||
|
|
||||||
err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out)
|
err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
return out.Mem, out.IsLittleEndian, nil
|
||||||
return out.Mem, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RPCClient) StopRecording() error {
|
func (c *RPCClient) StopRecording() error {
|
||||||
|
|||||||
@ -833,7 +833,8 @@ type ExamineMemoryIn struct {
|
|||||||
|
|
||||||
// ExaminedMemoryOut holds the return values of ExamineMemory
|
// ExaminedMemoryOut holds the return values of ExamineMemory
|
||||||
type ExaminedMemoryOut struct {
|
type ExaminedMemoryOut struct {
|
||||||
Mem []byte
|
Mem []byte
|
||||||
|
IsLittleEndian bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) error {
|
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.Mem = Mem
|
||||||
|
out.IsLittleEndian = true //TODO: get byte order from debugger.target.BinInfo().Arch
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user