Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: dump byte slice memory contents into file #3706

Open
stapelberg opened this issue Apr 16, 2024 · 0 comments · May be fixed by #3721
Open

Feature request: dump byte slice memory contents into file #3706

stapelberg opened this issue Apr 16, 2024 · 0 comments · May be fixed by #3721

Comments

@stapelberg
Copy link
Contributor

[Using dlv 1.22.1]

Recently, I had to debug a number of failing Go batch pipelines. Because of how these pipelines are structured, it is prohibitively complicated to run them locally, so all I had was a core dump.

With a running program, I would expect call os.WriteFile("/tmp/b.bin", b, 0600) to write the contents of the byte slice b to /tmp/b.bin. Unfortunately this didn’t actually work in my test, but that’s probably worth a separate issue:

(dlv) call os.WriteFile("/tmp/b.bin", b, 0644)
> [runtime-fatal-throw] runtime.fatalsignal() third_party/go/gc/src/runtime/signal_unix.go:795 (hits goroutine(1):1 total:1) (PC: 0x558a764f39ae)
Warning: debugging optimized function
Command failed: could not find symbol value for os

Anyway, because I only have a core dump, I needed to find another way to dump the contents.

Luckily, I found https://www.youtube.com/watch?v=sV5f1dF8ZU0, which explains how to display the Go slice header:

(dlv) x -size 8 -count 3 -x &b
0xc004406e30:   0x000000c005c80000   0x0000000000001d6c   0x000000000002ca2b

With the memory address and element count, we can try to display the slice contents:

(dlv) x -size 1 -count 0x1d6c 0x000000c005c80000
Command failed: count/len must be a positive integer
(dlv) x -size 1 -count 7532 0x000000c005c80000
Command failed: read memory range (count*size) must be less than or equal to 1000 bytes

I removed the 1000 bytes check from the code:

diff --git i/pkg/terminal/command.go w/pkg/terminal/command.go
index dc629437..4c0c19d6 100644
--- i/pkg/terminal/command.go
+++ w/pkg/terminal/command.go
@@ -2131,9 +2131,9 @@ 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 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")

diff --git i/service/rpc2/server.go w/service/rpc2/server.go
index f806873b..cbcd4d18 100644
--- i/service/rpc2/server.go
+++ w/service/rpc2/server.go
@@ -977,9 +977,9 @@ type ExaminedMemoryOut struct {
 }
 
 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")
-	}
+	// 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

Now, dlv lets me print the memory contents, which I can redirect into a file using the transcript command:

(dlv) transcript /tmp/b.hex
(dlv) x -size 1 -count 7532 0xc005c80000
0xc005d80000:   0x68  0x65   0x6c   0x6c   0x6f   0x20   0x63   0x75   
[…]
(dlv) transcript -off

However, dlv’s current format is not compatible with xxd -r, so would require further work to turn from hex back to binary.

I found it easier to modify dlv’s printing:

diff --git i/service/api/prettyprint.go w/service/api/prettyprint.go
index 001ab17f..8d98d7e9 100644
--- i/service/api/prettyprint.go
+++ w/service/api/prettyprint.go
@@ -511,12 +511,12 @@ func PrettyExamineMemory(address uintptr, memArea []byte, isLittleEndian bool, f
 		cols = 8
 		colFormat = fmt.Sprintf("%%0%dd", colBytes*3)
 	case 'x':
-		cols = 8
-		colFormat = fmt.Sprintf("0x%%0%dx", colBytes*2) // Always keep one leading '0x' for hex.
+		cols = 16
+		colFormat = "%02x"
 	default:
 		return fmt.Sprintf("not supported format %q\n", string(format))
 	}
-	colFormat += "\t"
+	colFormat += " "
 
 	l := len(memArea)
 	rows := l / (cols * colBytes)
@@ -528,13 +528,14 @@ func PrettyExamineMemory(address uintptr, memArea []byte, isLittleEndian bool, f
 	if l != 0 {
 		addrLen = len(fmt.Sprintf("%x", uint64(address)+uint64(l)))
 	}
-	addrFmt = "0x%0" + strconv.Itoa(addrLen) + "x:\t"
+	addrFmt = "%0" + strconv.Itoa(addrLen) + "x: "
 
 	var b strings.Builder
 	w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
 
+	startAddress := address
 	for i := 0; i < rows; i++ {
-		fmt.Fprintf(w, addrFmt, address)
+		fmt.Fprintf(w, addrFmt, address-startAddress)
 
 		for j := 0; j < cols; j++ {
 			offset := i*(cols*colBytes) + j*colBytes

With this modification, dlv will print like so:

(dlv) transcript /tmp/b.hex
(dlv) x -size 1 -count 7532 0xc005c80000
0000000000: 68 65 6c 6c 6f 20 63 75  72 69 6f 75 73 20 67 6f
[…]
(dlv) transcript -off

…and then I can use xxd -r /tmp/b.hex > /tmp/b.bin (after deleting the command itself from /tmp/b.hex).


My feature request is for a command built into delve which can dump memory into a file. It would be tremendously useful and is rather hard to accomplish today (see above).

Thanks

aarzilli added a commit to aarzilli/delve that referenced this issue May 8, 2024
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 go-delve#3706
@aarzilli aarzilli linked a pull request May 8, 2024 that will close this issue
aarzilli added a commit to aarzilli/delve that referenced this issue May 13, 2024
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 go-delve#3706
aarzilli added a commit to aarzilli/delve that referenced this issue May 14, 2024
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 go-delve#3706
aarzilli added a commit to aarzilli/delve that referenced this issue May 15, 2024
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 go-delve#3706
aarzilli added a commit to aarzilli/delve that referenced this issue May 23, 2024
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 go-delve#3706
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants