You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
5.1 KiB
Markdown
297 lines
5.1 KiB
Markdown
4 months ago
|
# Using the Debugger
|
||
|
|
||
|
Go doesn't ship with a debugger but you can use the delve package instead.
|
||
|
|
||
|
```
|
||
|
go get github.com/go-delve/delve/cmd/dlv
|
||
|
```
|
||
|
|
||
|
```
|
||
|
dlv version
|
||
|
Delve Debugger
|
||
|
Version: 1.23.1
|
||
|
Build: $Id: 2eba762d75437d380e48fc42213853f13aa2904d $
|
||
|
```
|
||
|
|
||
|
## Using dlv
|
||
|
|
||
|
From within a go module run:
|
||
|
|
||
|
```
|
||
|
dlv debug
|
||
|
|
||
|
Type 'help' for list of commands.
|
||
|
(dlv)
|
||
|
```
|
||
|
|
||
|
Now set a breakpoint on the main function
|
||
|
|
||
|
```
|
||
|
break main.main
|
||
|
```
|
||
|
|
||
|
This breaks not at your main function but in the go runtime.
|
||
|
|
||
|
Now you can do something like set a break point at a line number in a file
|
||
|
|
||
|
```
|
||
|
break main.go:20
|
||
|
```
|
||
|
|
||
|
Now we can continue until we hit a breakpoint
|
||
|
|
||
|
```
|
||
|
c or continue
|
||
|
|
||
|
|
||
|
> [Breakpoint 1] main.main() ./main.go:19 (hits goroutine(1):1 total:1) (PC: 0x4ae66e)
|
||
|
14:
|
||
|
15: func Three() {
|
||
|
16: fmt.Println("Three!")
|
||
|
17: }
|
||
|
18:
|
||
|
=> 19: func main() {
|
||
|
20: msg := "Hello debugger"
|
||
|
21: One()
|
||
|
22: fmt.Println(msg)
|
||
|
23:
|
||
|
24: }
|
||
|
```
|
||
|
|
||
|
Now we can check for locals
|
||
|
|
||
|
```
|
||
|
locals
|
||
|
|
||
|
(no locals)
|
||
|
```
|
||
|
|
||
|
No surprising there are no local variables at this point.
|
||
|
|
||
|
```
|
||
|
n
|
||
|
```
|
||
|
|
||
|
Will advance us to the next line. But again no locals because that hasn't been executed yet. So `n` again.
|
||
|
|
||
|
```
|
||
|
locals
|
||
|
|
||
|
msg = "Hello debugger"
|
||
|
```
|
||
|
|
||
|
|
||
|
|
||
|
```
|
||
|
ls
|
||
|
|
||
|
> main.main() ./main.go:21 (PC: 0x4ae687)
|
||
|
16: fmt.Println("Three!")
|
||
|
17: }
|
||
|
18:
|
||
|
19: func main() {
|
||
|
20: msg := "Hello debugger"
|
||
|
=> 21: One()
|
||
|
22: fmt.Println(msg)
|
||
|
23:
|
||
|
24: }
|
||
|
```
|
||
|
|
||
|
Now we can step into our function with `s`
|
||
|
|
||
|
```
|
||
|
s
|
||
|
|
||
|
> main.One() ./main.go:5 (PC: 0x4ae48a)
|
||
|
1: package main
|
||
|
2:
|
||
|
3: import "fmt"
|
||
|
4:
|
||
|
=> 5: func One() {
|
||
|
6: Two()
|
||
|
7: fmt.Println("One!")
|
||
|
8: }
|
||
|
9:
|
||
|
10: func Two() {
|
||
|
```
|
||
|
|
||
|
You can se now we have stepped into the function.
|
||
|
|
||
|
We can `n` and `s` into `Two()`.
|
||
|
|
||
|
```
|
||
|
> main.Two() ./main.go:10 (PC: 0x4ae52a)
|
||
|
5: func One() {
|
||
|
6: Two()
|
||
|
7: fmt.Println("One!")
|
||
|
8: }
|
||
|
9:
|
||
|
=> 10: func Two() {
|
||
|
11: Three()
|
||
|
12: fmt.Println("Two!")
|
||
|
13: }
|
||
|
14:
|
||
|
15: func Three() {
|
||
|
```
|
||
|
|
||
|
How about we look at the stack trace at this point
|
||
|
|
||
|
```
|
||
|
stack
|
||
|
0 0x00000000004ae52a in main.Two
|
||
|
at ./main.go:10
|
||
|
1 0x00000000004ae493 in main.One
|
||
|
at ./main.go:6
|
||
|
2 0x00000000004ae68c in main.main
|
||
|
at ./main.go:21
|
||
|
3 0x000000000043b647 in runtime.main
|
||
|
at /usr/local/go/src/runtime/proc.go:272
|
||
|
4 0x0000000000474081 in runtime.goexit
|
||
|
at /usr/local/go/src/runtime/asm_amd64.s:1700
|
||
|
```
|
||
|
|
||
|
Let's `n` and `s` one more time and check the stack trace.
|
||
|
|
||
|
```
|
||
|
0 0x00000000004ae5ca in main.Three
|
||
|
at ./main.go:15
|
||
|
1 0x00000000004ae533 in main.Two
|
||
|
at ./main.go:11
|
||
|
2 0x00000000004ae493 in main.One
|
||
|
at ./main.go:6
|
||
|
3 0x00000000004ae68c in main.main
|
||
|
at ./main.go:21
|
||
|
4 0x000000000043b647 in runtime.main
|
||
|
at /usr/local/go/src/runtime/proc.go:272
|
||
|
5 0x0000000000474081 in runtime.goexit
|
||
|
at /usr/local/go/src/runtime/asm_amd64.s:1700
|
||
|
```
|
||
|
|
||
|
If we needed to we could us `frame` to go back into the stack and and we can inspect the state of code at that point.
|
||
|
|
||
|
|
||
|
```
|
||
|
frame 3
|
||
|
|
||
|
locals
|
||
|
```
|
||
|
|
||
|
But then we need to move back to `frame 0` and can continue debugging.
|
||
|
|
||
|
```
|
||
|
(dlv) ls
|
||
|
> main.Two() ./main.go:11 (PC: 0x4ae52e)
|
||
|
6: Two()
|
||
|
7: fmt.Println("One!")
|
||
|
8: }
|
||
|
9:
|
||
|
10: func Two() {
|
||
|
=> 11: Three()
|
||
|
12: fmt.Println("Two!")
|
||
|
13: }
|
||
|
14:
|
||
|
15: func Three() {
|
||
|
16: fmt.Println("Three!")
|
||
|
```
|
||
|
|
||
|
Instead of stepping to `Three()` let's step out of Two.
|
||
|
|
||
|
```
|
||
|
(dlv) so
|
||
|
Three!
|
||
|
Two!
|
||
|
> main.One() ./main.go:7 (PC: 0x4ae493)
|
||
|
Values returned:
|
||
|
|
||
|
2:
|
||
|
3: import "fmt"
|
||
|
4:
|
||
|
5: func One() {
|
||
|
6: Two()
|
||
|
=> 7: fmt.Println("One!")
|
||
|
8: }
|
||
|
9:
|
||
|
10: func Two() {
|
||
|
11: Three()
|
||
|
12: fmt.Println("Two!")
|
||
|
```
|
||
|
|
||
|
We see out stdout messages now there is one to go.
|
||
|
|
||
|
```
|
||
|
(dlv) stack
|
||
|
0 0x00000000004ae493 in main.One
|
||
|
at ./main.go:7
|
||
|
1 0x00000000004ae68c in main.main
|
||
|
at ./main.go:21
|
||
|
2 0x000000000043b647 in runtime.main
|
||
|
at /usr/local/go/src/runtime/proc.go:272
|
||
|
3 0x0000000000474081 in runtime.goexit
|
||
|
at /usr/local/go/src/runtime/asm_amd64.s:1700
|
||
|
```
|
||
|
|
||
|
We can continue this until our process returns. But when we are back in our main function. Let's look at locals one more time.
|
||
|
|
||
|
```
|
||
|
17: }
|
||
|
18:
|
||
|
19: func main() {
|
||
|
20: msg := "Hello debugger"
|
||
|
21: One()
|
||
|
=> 22: fmt.Println(msg)
|
||
|
23:
|
||
|
24: }
|
||
|
|
||
|
locals
|
||
|
|
||
|
msg = "Hello debugger"
|
||
|
```
|
||
|
|
||
|
We can't change this variable because Delve wants to avoid operations itself that involves memory allocations, but if we had a
|
||
|
|
||
|
```
|
||
|
func setMsg(value string) {
|
||
|
msg = value
|
||
|
}
|
||
|
```
|
||
|
|
||
|
We could use `call` to set that variable
|
||
|
|
||
|
```
|
||
|
call setMsg("dirp")
|
||
|
```
|
||
|
|
||
|
To close it could just `continue` until the process exits.
|
||
|
|
||
|
```
|
||
|
(dlv) c
|
||
|
|
||
|
Process 29036 has exited with status 0
|
||
|
```
|
||
|
|
||
|
## Wait there is more
|
||
|
|
||
|
Help has tons of stuff
|
||
|
|
||
|
```
|
||
|
help
|
||
|
```
|
||
|
|
||
|
Like
|
||
|
|
||
|
```
|
||
|
restart
|
||
|
```
|
||
|
Restarts the program of course. Our current breakpoints are still set.
|
||
|
|
||
|
```
|
||
|
(dlv) print msg
|
||
|
"Hello debugger"
|
||
|
```
|
||
|
|
||
|
And hey this time we used print to check a value.
|
||
|
|
||
|
Technically you can edit code with `edit`.
|
||
|
|
||
|
|
||
|
## Debugging goroutines
|