From 547812076aefdbffe50639eb3bc02879e61f373f Mon Sep 17 00:00:00 2001 From: aarzilli Date: Wed, 8 May 2024 16:24:41 +0200 Subject: [PATCH] terminal,service: add raw examinemem dump Change the examinemem command to have a new format 'raw' that just prints the raw memory bytes. Change the transcript command to add a new flag that disables prompt echo to the output file. Fixes #3706 --- Documentation/cli/README.md | 6 +-- pkg/terminal/command.go | 81 +++++++++++++++++++++++-------------- pkg/terminal/out.go | 8 ++-- service/rpc2/server.go | 4 +- 4 files changed, 61 insertions(+), 38 deletions(-) diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md index 7cf266dd0b..079f369254 100644 --- a/Documentation/cli/README.md +++ b/Documentation/cli/README.md @@ -354,7 +354,7 @@ Examine memory: examinemem [-fmt ] [-count|-len ] [-size ]
examinemem [-fmt ] [-count|-len ] [-size ] -x -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) and raw. 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. @@ -741,10 +741,10 @@ Aliases: t ## transcript Appends command output to a file. - transcript [-t] [-x] + transcript [-t] [-x] [-n] transcript -off -Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed instead. +Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed. If [-n] input echo to the transcript file is disabled. Using the -off option disables the transcript. diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index 524c4a8582..1d5653a010 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -586,7 +586,7 @@ Examine memory: examinemem [-fmt ] [-count|-len ] [-size ]
examinemem [-fmt ] [-count|-len ] [-size ] -x -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) and raw. 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. @@ -615,10 +615,10 @@ The core dump is always written in ELF, even on systems (windows, macOS) where t {aliases: []string{"transcript"}, cmdFn: transcript, helpMsg: `Appends command output to a file. - transcript [-t] [-x] + transcript [-t] [-x] [-n] transcript -off -Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed instead. +Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed. If [-n] input echo to the transcript file is disabled. Using the -off option disables the transcript.`}, @@ -2057,9 +2057,10 @@ func examineMemoryCmd(t *Term, ctx callContext, argstr string) error { // Default value priFmt := byte('x') - count := 1 - size := 1 + count := int64(1) + size := int64(1) isExpr := false + rawout := false // nextArg returns the next argument that is not an empty string, if any, and // advances the args slice to the position after that. @@ -2085,19 +2086,23 @@ loop: if arg == "" { 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[arg] - if !ok { - return fmt.Errorf("%q is not a valid format", arg) + if arg == "raw" { + rawout = true + } else { + fmtMapToPriFmt := map[string]byte{ + "oct": 'o', + "octal": 'o', + "hex": 'x', + "hexadecimal": 'x', + "dec": 'd', + "decimal": 'd', + "bin": 'b', + "binary": 'b', + } + priFmt, ok = fmtMapToPriFmt[arg] + if !ok { + return fmt.Errorf("%q is not a valid format", arg) + } } case "-count", "-len": arg := nextArg() @@ -2105,7 +2110,7 @@ loop: return fmt.Errorf("expected argument after -count/-len") } var err error - count, err = strconv.Atoi(arg) + count, err = strconv.ParseInt(arg, 0, 64) if err != nil || count <= 0 { return fmt.Errorf("count/len must be a positive integer") } @@ -2115,7 +2120,7 @@ loop: return fmt.Errorf("expected argument after -size") } var err error - size, err = strconv.Atoi(arg) + size, err = strconv.ParseInt(arg, 0, 64) if err != nil || size <= 0 || size > 8 { return fmt.Errorf("size must be a positive integer (<=8)") } @@ -2131,11 +2136,6 @@ loop: } } - // 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 len(args) == 0 { return fmt.Errorf("no address specified") } @@ -2169,12 +2169,28 @@ loop: } } - memArea, isLittleEndian, err := t.client.ExamineMemory(address, count*size) - if err != nil { - return err - } t.stdout.pw.PageMaybe(nil) - fmt.Fprint(t.stdout, api.PrettyExamineMemory(uintptr(address), memArea, isLittleEndian, priFmt, size)) + + start := address + remsz := int(count * size) + + for remsz > 0 { + reqsz := rpc2.ExamineMemoryLengthLimit + if reqsz > remsz { + reqsz = remsz + } + memArea, isLittleEndian, err := t.client.ExamineMemory(start, reqsz) + if err != nil { + return err + } + if rawout { + t.stdout.Write(memArea) + } else { + fmt.Fprint(t.stdout, api.PrettyExamineMemory(uintptr(start), memArea, isLittleEndian, priFmt, int(size))) + } + start += uint64(reqsz) + remsz -= reqsz + } return nil } @@ -3355,9 +3371,12 @@ func transcript(t *Term, ctx callContext, args string) error { truncate := false fileOnly := false disable := false + echo := true path := "" for _, arg := range argv { switch arg { + case "-n": + echo = false case "-x": fileOnly = true case "-t": @@ -3397,7 +3416,7 @@ func transcript(t *Term, ctx callContext, args string) error { return err } - t.stdout.TranscribeTo(fh, fileOnly) + t.stdout.TranscribeTo(fh, fileOnly, echo) return nil } diff --git a/pkg/terminal/out.go b/pkg/terminal/out.go index 79dd73f9ae..b383e259a8 100644 --- a/pkg/terminal/out.go +++ b/pkg/terminal/out.go @@ -14,7 +14,8 @@ import ( // transcriptWriter writes to a pagingWriter and also, optionally, to a // buffered file. type transcriptWriter struct { - fileOnly bool + fileOnly, echo bool + pw *pagingWriter file *bufio.Writer fh io.Closer @@ -52,7 +53,7 @@ func (w *transcriptWriter) ColorizePrint(path string, reader io.ReadSeeker, star // Echo outputs str only to the optional transcript file. func (w *transcriptWriter) Echo(str string) { - if w.file != nil { + if w.echo && w.file != nil { w.file.WriteString(str) } } @@ -80,13 +81,14 @@ func (w *transcriptWriter) CloseTranscript() error { // TranscribeTo starts transcribing the output to the specified file. If // fileOnly is true the output will only go to the file, output to the // io.Writer will be suppressed. -func (w *transcriptWriter) TranscribeTo(fh io.WriteCloser, fileOnly bool) { +func (w *transcriptWriter) TranscribeTo(fh io.WriteCloser, fileOnly, echo bool) { if w.file == nil { w.CloseTranscript() } w.fh = fh w.file = bufio.NewWriter(fh) w.fileOnly = fileOnly + w.echo = echo } // pagingWriter writes to w. If PageMaybe is called, after a large amount of diff --git a/service/rpc2/server.go b/service/rpc2/server.go index f806873bdc..2387b6efbf 100644 --- a/service/rpc2/server.go +++ b/service/rpc2/server.go @@ -976,8 +976,10 @@ type ExaminedMemoryOut struct { IsLittleEndian bool } +const ExamineMemoryLengthLimit = 1 << 16 + func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) error { - if arg.Length > 1000 { + if arg.Length > ExamineMemoryLengthLimit { return fmt.Errorf("len must be less than or equal to 1000") } Mem, err := s.debugger.ExamineMemory(arg.Address, arg.Length)