examinememory: evaluate addr as expression (#2385)
* examinememory: evaluate addr as expression This makes it easy to read memory locations at an offset of a known address, e.g.: x 0xc000046800 + 32 * use feedback from @aarzilli - expression mode is now enabled via -x flag - support "-x var", "-x &var" in addition to "-x <addr expr>" - some refactoring * add test cases * deal with double spaces * update docs * add new failing test * fix docs * simplify implementation, update test & docs * Fix docs
This commit is contained in:
parent
bbae9a9d12
commit
7bf5482b32
@ -280,14 +280,19 @@ Aliases: ed
|
|||||||
Examine memory:
|
Examine memory:
|
||||||
|
|
||||||
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
|
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
|
||||||
|
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] -x <expression>
|
||||||
|
|
||||||
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).
|
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. Please note '-len' is deprecated by '-count and -size'.
|
Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
|
||||||
|
Expression can be an integer expression or pointer value of the memory location to examine.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
x -fmt hex -count 20 -size 1 0xc00008af38
|
x -fmt hex -count 20 -size 1 0xc00008af38
|
||||||
|
x -fmt hex -count 20 -size 1 -x 0xc00008af38 + 8
|
||||||
|
x -fmt hex -count 20 -size 1 -x &myVar
|
||||||
|
x -fmt hex -count 20 -size 1 -x myPtrVar
|
||||||
|
|
||||||
Aliases: x
|
Aliases: x
|
||||||
|
|
||||||
|
@ -408,14 +408,19 @@ 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>] [-count|-len <count>] [-size <size>] <address>
|
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
|
||||||
|
examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] -x <expression>
|
||||||
|
|
||||||
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).
|
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. Please note '-len' is deprecated by '-count and -size'.
|
Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
|
||||||
|
Expression can be an integer expression or pointer value of the memory location to examine.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
x -fmt hex -count 20 -size 1 0xc00008af38`},
|
x -fmt hex -count 20 -size 1 0xc00008af38
|
||||||
|
x -fmt hex -count 20 -size 1 -x 0xc00008af38 + 8
|
||||||
|
x -fmt hex -count 20 -size 1 -x &myVar
|
||||||
|
x -fmt hex -count 20 -size 1 -x myPtrVar`},
|
||||||
|
|
||||||
{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.
|
||||||
|
|
||||||
@ -1659,27 +1664,42 @@ func edit(t *Term, ctx callContext, args string) error {
|
|||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
func examineMemoryCmd(t *Term, ctx callContext, argstr string) error {
|
||||||
v := strings.FieldsFunc(args, func(c rune) bool {
|
|
||||||
return c == ' '
|
|
||||||
})
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
address uint64
|
address uint64
|
||||||
err error
|
err error
|
||||||
ok bool
|
ok bool
|
||||||
|
args = strings.Split(argstr, " ")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default value
|
// Default value
|
||||||
priFmt := byte('x')
|
priFmt := byte('x')
|
||||||
count := 1
|
count := 1
|
||||||
size := 1
|
size := 1
|
||||||
|
isExpr := false
|
||||||
|
|
||||||
for i := 0; i < len(v); i++ {
|
// nextArg returns the next argument that is not an empty string, if any, and
|
||||||
switch v[i] {
|
// advances the args slice to the position after that.
|
||||||
|
nextArg := func() string {
|
||||||
|
for len(args) > 0 {
|
||||||
|
arg := args[0]
|
||||||
|
args = args[1:]
|
||||||
|
if arg != "" {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
switch cmd := nextArg(); cmd {
|
||||||
|
case "":
|
||||||
|
// no more arguments
|
||||||
|
break loop
|
||||||
case "-fmt":
|
case "-fmt":
|
||||||
i++
|
arg := nextArg()
|
||||||
if i >= len(v) {
|
if arg == "" {
|
||||||
return fmt.Errorf("expected argument after -fmt")
|
return fmt.Errorf("expected argument after -fmt")
|
||||||
}
|
}
|
||||||
fmtMapToPriFmt := map[string]byte{
|
fmtMapToPriFmt := map[string]byte{
|
||||||
@ -1692,39 +1712,39 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
|||||||
"bin": 'b',
|
"bin": 'b',
|
||||||
"binary": 'b',
|
"binary": 'b',
|
||||||
}
|
}
|
||||||
priFmt, ok = fmtMapToPriFmt[v[i]]
|
priFmt, ok = fmtMapToPriFmt[arg]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%q is not a valid format", v[i])
|
return fmt.Errorf("%q is not a valid format", arg)
|
||||||
}
|
}
|
||||||
case "-count", "-len":
|
case "-count", "-len":
|
||||||
i++
|
arg := nextArg()
|
||||||
if i >= len(v) {
|
if arg == "" {
|
||||||
return fmt.Errorf("expected argument after -count/-len")
|
return fmt.Errorf("expected argument after -count/-len")
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
count, err = strconv.Atoi(v[i])
|
count, err = strconv.Atoi(arg)
|
||||||
if err != nil || count <= 0 {
|
if err != nil || count <= 0 {
|
||||||
return fmt.Errorf("count/len must be a positive integer")
|
return fmt.Errorf("count/len must be a positive integer")
|
||||||
}
|
}
|
||||||
case "-size":
|
case "-size":
|
||||||
i++
|
arg := nextArg()
|
||||||
if i >= len(v) {
|
if arg == "" {
|
||||||
return fmt.Errorf("expected argument after -size")
|
return fmt.Errorf("expected argument after -size")
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
size, err = strconv.Atoi(v[i])
|
size, err = strconv.Atoi(arg)
|
||||||
if err != nil || size <= 0 || size > 8 {
|
if err != nil || size <= 0 || size > 8 {
|
||||||
return fmt.Errorf("size must be a positive integer (<=8)")
|
return fmt.Errorf("size must be a positive integer (<=8)")
|
||||||
}
|
}
|
||||||
|
case "-x":
|
||||||
|
isExpr = true
|
||||||
|
break loop // remaining args are going to be interpreted as expression
|
||||||
default:
|
default:
|
||||||
if i != len(v)-1 {
|
if len(args) > 0 {
|
||||||
return fmt.Errorf("unknown option %q", v[i])
|
return fmt.Errorf("unknown option %q", args[0])
|
||||||
}
|
|
||||||
// TODO, maybe we can support expression.
|
|
||||||
address, err = strconv.ParseUint(v[len(v)-1], 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("convert address into uintptr type failed, %s", err)
|
|
||||||
}
|
}
|
||||||
|
args = []string{cmd}
|
||||||
|
break loop // only one arg left to be evaluated as a uint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1733,10 +1753,39 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
|||||||
return fmt.Errorf("read memory range (count*size) must be less than or equal to 1000 bytes")
|
return fmt.Errorf("read memory range (count*size) must be less than or equal to 1000 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
if address == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf("no address specified")
|
return fmt.Errorf("no address specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isExpr {
|
||||||
|
expr := strings.Join(args, " ")
|
||||||
|
val, err := t.client.EvalVariable(ctx.Scope, expr, t.loadConfig())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// "-x &myVar" or "-x myPtrVar"
|
||||||
|
if val.Kind == reflect.Ptr {
|
||||||
|
if len(val.Children) < 1 {
|
||||||
|
return fmt.Errorf("bug? invalid pointer: %#v", val)
|
||||||
|
}
|
||||||
|
address = val.Children[0].Addr
|
||||||
|
// "-x 0xc000079f20 + 8" or -x 824634220320 + 8
|
||||||
|
} else if val.Kind == reflect.Int && val.Value != "" {
|
||||||
|
address, err = strconv.ParseUint(val.Value, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("bad expression result: %q: %s", val.Value, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unsupported expression type: %s", val.Kind)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
address, err = strconv.ParseUint(args[0], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert address into uintptr type failed, %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
memArea, isLittleEndian, err := t.client.ExamineMemory(address, count*size)
|
memArea, isLittleEndian, err := t.client.ExamineMemory(address, count*size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1029,6 +1029,66 @@ func TestExamineMemoryCmd(t *testing.T) {
|
|||||||
if !strings.Contains(res, firstLine) {
|
if !strings.Contains(res, firstLine) {
|
||||||
t.Fatalf("expected first line: %s", firstLine)
|
t.Fatalf("expected first line: %s", firstLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// third examining memory: -x addr
|
||||||
|
res = term.MustExec("examinemem -x " + addressStr)
|
||||||
|
t.Logf("the third result of examining memory result \n%s", res)
|
||||||
|
firstLine = fmt.Sprintf("%#x: 0xff", address)
|
||||||
|
if !strings.Contains(res, firstLine) {
|
||||||
|
t.Fatalf("expected first line: %s", firstLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fourth examining memory: -x addr + offset
|
||||||
|
res = term.MustExec("examinemem -x " + addressStr + " + 8")
|
||||||
|
t.Logf("the fourth result of examining memory result \n%s", res)
|
||||||
|
firstLine = fmt.Sprintf("%#x: 0x12", address+8)
|
||||||
|
if !strings.Contains(res, firstLine) {
|
||||||
|
t.Fatalf("expected first line: %s", firstLine)
|
||||||
|
}
|
||||||
|
// fifth examining memory: -x &var
|
||||||
|
res = term.MustExec("examinemem -x &bs[0]")
|
||||||
|
t.Logf("the fifth result of examining memory result \n%s", res)
|
||||||
|
firstLine = fmt.Sprintf("%#x: 0xff", address)
|
||||||
|
if !strings.Contains(res, firstLine) {
|
||||||
|
t.Fatalf("expected first line: %s", firstLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sixth examining memory: -fmt and double spaces
|
||||||
|
res = term.MustExec("examinemem -fmt hex -x &bs[0]")
|
||||||
|
t.Logf("the sixth result of examining memory result \n%s", res)
|
||||||
|
firstLine = fmt.Sprintf("%#x: 0xff", address)
|
||||||
|
if !strings.Contains(res, firstLine) {
|
||||||
|
t.Fatalf("expected first line: %s", firstLine)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
|
||||||
|
tests := []struct {
|
||||||
|
Expr string
|
||||||
|
Want int
|
||||||
|
}{
|
||||||
|
{Expr: "&i1", Want: 1},
|
||||||
|
{Expr: "&i2", Want: 2},
|
||||||
|
{Expr: "p1", Want: 1},
|
||||||
|
{Expr: "*pp1", Want: 1},
|
||||||
|
{Expr: "&str1[1]", Want: '1'},
|
||||||
|
{Expr: "c1.pb", Want: 1},
|
||||||
|
{Expr: "&c1.pb.a", Want: 1},
|
||||||
|
{Expr: "&c1.pb.a.A", Want: 1},
|
||||||
|
{Expr: "&c1.pb.a.B", Want: 2},
|
||||||
|
}
|
||||||
|
term.MustExec("continue")
|
||||||
|
for _, test := range tests {
|
||||||
|
res := term.MustExec("examinemem -fmt dec -x " + test.Expr)
|
||||||
|
// strip addr from output, e.g. "0xc0000160b8: 023" -> "023"
|
||||||
|
res = strings.TrimSpace(strings.Split(res, ":")[1])
|
||||||
|
got, err := strconv.Atoi(res)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expr=%q err=%s", test.Expr, err)
|
||||||
|
} else if got != test.Want {
|
||||||
|
t.Errorf("expr=%q got=%d want=%d", test.Expr, got, test.Want)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user