pkg,service: add cmd examinemem
(x
) for examining memory. (#1814)
According to #1800 #1584 #1038, `dlv` should enable the user to dive into memory. User can print binary data in specific memory address range. But not support for sepecific variable name or structures temporarily.(Because I have no idea that modify `print` command.) Close #1584.
This commit is contained in:
parent
5b4f4a81b1
commit
a5d9dbee79
@ -24,6 +24,7 @@ Command | Description
|
|||||||
[disassemble](#disassemble) | Disassembler.
|
[disassemble](#disassemble) | Disassembler.
|
||||||
[down](#down) | Move the current frame down.
|
[down](#down) | Move the current frame down.
|
||||||
[edit](#edit) | Open where you are in $DELVE_EDITOR or $EDITOR
|
[edit](#edit) | Open where you are in $DELVE_EDITOR or $EDITOR
|
||||||
|
[examinemem](#examinemem) | Examine memory:
|
||||||
[exit](#exit) | Exit the debugger.
|
[exit](#exit) | Exit the debugger.
|
||||||
[frame](#frame) | Set the current frame, or execute command on a different frame.
|
[frame](#frame) | Set the current frame, or execute command on a different frame.
|
||||||
[funcs](#funcs) | Print list of functions.
|
[funcs](#funcs) | Print list of functions.
|
||||||
@ -210,6 +211,18 @@ If locspec is omitted edit will open the current source file in the editor, othe
|
|||||||
|
|
||||||
Aliases: ed
|
Aliases: ed
|
||||||
|
|
||||||
|
## examinemem
|
||||||
|
Examine memory:
|
||||||
|
|
||||||
|
examinemem [-fmt <format>] [-len <length>] <address>
|
||||||
|
|
||||||
|
Format represents the data format and the value is one of this list (default hex): oct(octal), hex(hexadecimal), dec(decimal), bin(binary).
|
||||||
|
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
|
||||||
|
|
||||||
|
Aliases: x
|
||||||
|
|
||||||
## exit
|
## exit
|
||||||
Exit the debugger.
|
Exit the debugger.
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ create_breakpoint(Breakpoint) | Equivalent to API call [CreateBreakpoint](https:
|
|||||||
detach(Kill) | Equivalent to API call [Detach](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Detach)
|
detach(Kill) | Equivalent to API call [Detach](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Detach)
|
||||||
disassemble(Scope, StartPC, EndPC, Flavour) | Equivalent to API call [Disassemble](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Disassemble)
|
disassemble(Scope, StartPC, EndPC, Flavour) | Equivalent to API call [Disassemble](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Disassemble)
|
||||||
eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Eval)
|
eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Eval)
|
||||||
|
examine_memory(Address, Length) | Equivalent to API call [ExamineMemory](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ExamineMemory)
|
||||||
find_location(Scope, Loc, IncludeNonExecutableLines) | Equivalent to API call [FindLocation](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation)
|
find_location(Scope, Loc, IncludeNonExecutableLines) | Equivalent to API call [FindLocation](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation)
|
||||||
function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations)
|
function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations)
|
||||||
get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint)
|
get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint)
|
||||||
|
25
_fixtures/examinememory.go
Normal file
25
_fixtures/examinememory.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l := int(51)
|
||||||
|
bs := make([]byte, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
bs[i] = byte(i + int(10))
|
||||||
|
}
|
||||||
|
|
||||||
|
bsp := (*byte)(unsafe.Pointer(&bs[0]))
|
||||||
|
|
||||||
|
bspUintptr := uintptr(unsafe.Pointer(bsp))
|
||||||
|
|
||||||
|
fmt.Printf("%#x\n", bspUintptr)
|
||||||
|
_ = *bsp
|
||||||
|
|
||||||
|
bs[0] = 255
|
||||||
|
|
||||||
|
_ = *bsp
|
||||||
|
}
|
@ -7,6 +7,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/cosiner/argv"
|
||||||
|
"github.com/go-delve/delve/service"
|
||||||
|
"github.com/go-delve/delve/service/api"
|
||||||
|
"github.com/go-delve/delve/service/debugger"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"io"
|
"io"
|
||||||
@ -20,11 +24,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/cosiner/argv"
|
|
||||||
"github.com/go-delve/delve/service"
|
|
||||||
"github.com/go-delve/delve/service/api"
|
|
||||||
"github.com/go-delve/delve/service/debugger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const optimizedFunctionWarning = "Warning: debugging optimized function"
|
const optimizedFunctionWarning = "Warning: debugging optimized function"
|
||||||
@ -368,6 +367,15 @@ Defines <alias> as an alias to <command> or removes an alias.`},
|
|||||||
|
|
||||||
If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
|
If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
|
||||||
{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
|
{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
|
||||||
|
|
||||||
|
{aliases: []string{"examinemem", "x"}, cmdFn: examineMemoryCmd, helpMsg: `Examine memory:
|
||||||
|
|
||||||
|
examinemem [-fmt <format>] [-len <length>] <address>
|
||||||
|
|
||||||
|
Format represents the data format and the value is one of this list (default hex): oct(octal), hex(hexadecimal), dec(decimal), bin(binary).
|
||||||
|
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`},
|
||||||
}
|
}
|
||||||
|
|
||||||
if client == nil || client.Recorded() {
|
if client == nil || client.Recorded() {
|
||||||
@ -1349,6 +1357,81 @@ func edit(t *Term, ctx callContext, args string) error {
|
|||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func examineMemoryCmd(t *Term, ctx callContext, args string) error {
|
||||||
|
v := strings.FieldsFunc(args, func(c rune) bool {
|
||||||
|
return c == ' '
|
||||||
|
})
|
||||||
|
|
||||||
|
var (
|
||||||
|
address int64
|
||||||
|
err error
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default value
|
||||||
|
priFmt := byte('x')
|
||||||
|
length := 1
|
||||||
|
|
||||||
|
for i := 0; i < len(v); i++ {
|
||||||
|
switch v[i] {
|
||||||
|
case "-fmt":
|
||||||
|
i++
|
||||||
|
if i >= len(v) {
|
||||||
|
return fmt.Errorf("expected argument after -fmt")
|
||||||
|
}
|
||||||
|
fmtMapToPriFmt := map[string]byte{
|
||||||
|
"oct": 'o',
|
||||||
|
"octal": 'o',
|
||||||
|
"hex": 'x',
|
||||||
|
"hexadecimal": 'x',
|
||||||
|
"dec": 'd',
|
||||||
|
"decimal": 'd',
|
||||||
|
"bin": 'b',
|
||||||
|
"binary": 'b',
|
||||||
|
}
|
||||||
|
priFmt, ok = fmtMapToPriFmt[v[i]]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%q is not a valid format", v[i])
|
||||||
|
}
|
||||||
|
case "-len":
|
||||||
|
i++
|
||||||
|
if i >= len(v) {
|
||||||
|
return fmt.Errorf("expected argument after -len")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
length, err = strconv.Atoi(v[i])
|
||||||
|
if err != nil || length <= 0 {
|
||||||
|
return fmt.Errorf("len must be an positive integer")
|
||||||
|
}
|
||||||
|
// TODO, maybe configured by user.
|
||||||
|
if length > 1000 {
|
||||||
|
return fmt.Errorf("len must be less than or equal to 1000")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if i != len(v)-1 {
|
||||||
|
return fmt.Errorf("unknown option %q", v[i])
|
||||||
|
}
|
||||||
|
// TODO, maybe we can support expression.
|
||||||
|
address, err = strconv.ParseInt(v[len(v)-1], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert address into uintptr type failed, %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if address == 0 {
|
||||||
|
return fmt.Errorf("no address specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
memArea, err := t.client.ExamineMemory(uintptr(address), length)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(api.PrettyExamineMemory(uintptr(address), memArea, priFmt))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func printVar(t *Term, ctx callContext, args string) error {
|
func printVar(t *Term, ctx callContext, args string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf("not enough arguments")
|
return fmt.Errorf("not enough arguments")
|
||||||
|
@ -993,3 +993,42 @@ func TestIssue1598(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExamineMemoryCmd(t *testing.T) {
|
||||||
|
withTestTerminal("examinememory", t, func(term *FakeTerminal) {
|
||||||
|
term.MustExec("break examinememory.go:19")
|
||||||
|
term.MustExec("break examinememory.go:24")
|
||||||
|
term.MustExec("continue")
|
||||||
|
|
||||||
|
addressStr := strings.TrimSpace(term.MustExec("p bspUintptr"))
|
||||||
|
address, err := strconv.ParseInt(addressStr, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could convert %s into int64, err %s", addressStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
if !strings.Contains(res, lastLine) {
|
||||||
|
t.Fatalf("expected last line: %s", lastLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// second examining memory
|
||||||
|
term.MustExec("continue")
|
||||||
|
res = term.MustExec("x -len 52 -fmt bin " + addressStr)
|
||||||
|
t.Logf("the second result of examining memory result \n%s", res)
|
||||||
|
|
||||||
|
// check first line
|
||||||
|
firstLine = fmt.Sprintf("%#x: 11111111 00001011 00001100 00001101", address)
|
||||||
|
if !strings.Contains(res, firstLine) {
|
||||||
|
t.Fatalf("expected first line: %s", firstLine)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -450,6 +450,44 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
}
|
}
|
||||||
return env.interfaceToStarlarkValue(rpcRet), nil
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
})
|
})
|
||||||
|
r["examine_memory"] = starlark.NewBuiltin("examine_memory", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
|
if err := isCancelled(thread); err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
var rpcArgs rpc2.ExamineMemoryIn
|
||||||
|
var rpcRet rpc2.ExaminedMemoryOut
|
||||||
|
if len(args) > 0 && args[0] != starlark.None {
|
||||||
|
err := unmarshalStarlarkValue(args[0], &rpcArgs.Address, "Address")
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(args) > 1 && args[1] != starlark.None {
|
||||||
|
err := unmarshalStarlarkValue(args[1], &rpcArgs.Length, "Length")
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, kv := range kwargs {
|
||||||
|
var err error
|
||||||
|
switch kv[0].(starlark.String) {
|
||||||
|
case "Address":
|
||||||
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Address, "Address")
|
||||||
|
case "Length":
|
||||||
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Length, "Length")
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown argument %q", kv[0])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := env.ctx.Client().CallAPI("ExamineMemory", &rpcArgs, &rpcRet)
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, err
|
||||||
|
}
|
||||||
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
|
})
|
||||||
r["find_location"] = starlark.NewBuiltin("find_location", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
r["find_location"] = starlark.NewBuiltin("find_location", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
if err := isCancelled(thread); err != nil {
|
if err := isCancelled(thread); err != nil {
|
||||||
return starlark.None, decorateError(thread, err)
|
return starlark.None, decorateError(thread, err)
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -354,3 +355,53 @@ 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 {
|
||||||
|
cols := 8
|
||||||
|
// Avoid emitting rows that are too long when using binary format
|
||||||
|
if format == 'b' {
|
||||||
|
cols = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
l := len(memArea)
|
||||||
|
rows := l / cols
|
||||||
|
if l%cols != 0 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := ""
|
||||||
|
for i := 0; i < rows; i++ {
|
||||||
|
lines += fmt.Sprintf("%#x:", 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:]
|
||||||
|
}
|
||||||
|
lines += curOutput
|
||||||
|
}
|
||||||
|
lines += "\n"
|
||||||
|
address += uintptr(cols)
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
@ -147,6 +147,11 @@ type Client interface {
|
|||||||
// ListDynamicLibraries returns a list of loaded dynamic libraries.
|
// ListDynamicLibraries returns a list of loaded dynamic libraries.
|
||||||
ListDynamicLibraries() ([]api.Image, error)
|
ListDynamicLibraries() ([]api.Image, error)
|
||||||
|
|
||||||
|
// 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 uintptr, length int) ([]byte, error)
|
||||||
|
|
||||||
// Disconnect closes the connection to the server without sending a Detach request first.
|
// Disconnect closes the connection to the server without sending a Detach request first.
|
||||||
// If cont is true a continue command will be sent instead.
|
// If cont is true a continue command will be sent instead.
|
||||||
Disconnect(cont bool) error
|
Disconnect(cont bool) error
|
||||||
|
@ -1313,6 +1313,25 @@ func (d *Debugger) ListDynamicLibraries() []api.Image {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExamineMemory returns the raw memory stored at the given address.
|
||||||
|
// The amount of data to be read is specified by length.
|
||||||
|
// This function will return an error if it reads less than `length` bytes.
|
||||||
|
func (d *Debugger) ExamineMemory(address uintptr, length int) ([]byte, error) {
|
||||||
|
d.processMutex.Lock()
|
||||||
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
|
thread := d.target.CurrentThread()
|
||||||
|
data := make([]byte, length)
|
||||||
|
n, err := thread.ReadMemory(data, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if length != n {
|
||||||
|
return nil, errors.New("the specific range has exceeded readable area")
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Debugger) GetVersion(out *api.GetVersionOut) error {
|
func (d *Debugger) GetVersion(out *api.GetVersionOut) error {
|
||||||
if d.config.CoreFile != "" {
|
if d.config.CoreFile != "" {
|
||||||
if d.config.Backend == "rr" {
|
if d.config.Backend == "rr" {
|
||||||
|
@ -413,6 +413,17 @@ func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
|
|||||||
return out.List, nil
|
return out.List, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *RPCClient) ExamineMemory(address uintptr, count int) ([]byte, error) {
|
||||||
|
out := &ExaminedMemoryOut{}
|
||||||
|
|
||||||
|
err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.Mem, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
||||||
return c.client.Call("RPCServer."+method, args, reply)
|
return c.client.Call("RPCServer."+method, args, reply)
|
||||||
}
|
}
|
||||||
|
@ -751,3 +751,27 @@ func (s *RPCServer) ListPackagesBuildInfo(in ListPackagesBuildInfoIn, out *ListP
|
|||||||
out.List = s.debugger.ListPackagesBuildInfo(in.IncludeFiles)
|
out.List = s.debugger.ListPackagesBuildInfo(in.IncludeFiles)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExamineMemoryIn holds the arguments of ExamineMemory
|
||||||
|
type ExamineMemoryIn struct {
|
||||||
|
Address uintptr
|
||||||
|
Length int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExaminedMemoryOut holds the return values of ExamineMemory
|
||||||
|
type ExaminedMemoryOut struct {
|
||||||
|
Mem []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) error {
|
||||||
|
if arg.Length > 1000 {
|
||||||
|
return fmt.Errorf("len must be less than or equal to 1000")
|
||||||
|
}
|
||||||
|
Mem, err := s.debugger.ExamineMemory(arg.Address, arg.Length)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Mem = Mem
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user