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:
|
||||
|
||||
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).
|
||||
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'.
|
||||
Expression can be an integer expression or pointer value of the memory location to examine.
|
||||
|
||||
For example:
|
||||
|
||||
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
|
||||
|
||||
|
@ -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:
|
||||
|
||||
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).
|
||||
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'.
|
||||
Expression can be an integer expression or pointer value of the memory location to examine.
|
||||
|
||||
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.
|
||||
|
||||
@ -1659,27 +1664,42 @@ func edit(t *Term, ctx callContext, args string) error {
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
||||
v := strings.FieldsFunc(args, func(c rune) bool {
|
||||
return c == ' '
|
||||
})
|
||||
|
||||
func examineMemoryCmd(t *Term, ctx callContext, argstr string) error {
|
||||
var (
|
||||
address uint64
|
||||
err error
|
||||
ok bool
|
||||
args = strings.Split(argstr, " ")
|
||||
)
|
||||
|
||||
// Default value
|
||||
priFmt := byte('x')
|
||||
count := 1
|
||||
size := 1
|
||||
isExpr := false
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
switch v[i] {
|
||||
// nextArg returns the next argument that is not an empty string, if any, and
|
||||
// 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":
|
||||
i++
|
||||
if i >= len(v) {
|
||||
arg := nextArg()
|
||||
if arg == "" {
|
||||
return fmt.Errorf("expected argument after -fmt")
|
||||
}
|
||||
fmtMapToPriFmt := map[string]byte{
|
||||
@ -1692,39 +1712,39 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
||||
"bin": 'b',
|
||||
"binary": 'b',
|
||||
}
|
||||
priFmt, ok = fmtMapToPriFmt[v[i]]
|
||||
priFmt, ok = fmtMapToPriFmt[arg]
|
||||
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":
|
||||
i++
|
||||
if i >= len(v) {
|
||||
arg := nextArg()
|
||||
if arg == "" {
|
||||
return fmt.Errorf("expected argument after -count/-len")
|
||||
}
|
||||
var err error
|
||||
count, err = strconv.Atoi(v[i])
|
||||
count, err = strconv.Atoi(arg)
|
||||
if err != nil || count <= 0 {
|
||||
return fmt.Errorf("count/len must be a positive integer")
|
||||
}
|
||||
case "-size":
|
||||
i++
|
||||
if i >= len(v) {
|
||||
arg := nextArg()
|
||||
if arg == "" {
|
||||
return fmt.Errorf("expected argument after -size")
|
||||
}
|
||||
var err error
|
||||
size, err = strconv.Atoi(v[i])
|
||||
size, err = strconv.Atoi(arg)
|
||||
if err != nil || size <= 0 || size > 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:
|
||||
if i != len(v)-1 {
|
||||
return fmt.Errorf("unknown option %q", v[i])
|
||||
}
|
||||
// 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)
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("unknown option %q", args[0])
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
if address == 0 {
|
||||
if len(args) == 0 {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1029,6 +1029,66 @@ func TestExamineMemoryCmd(t *testing.T) {
|
||||
if !strings.Contains(res, 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