Adding debugger notes
parent
c4e7f0d5ea
commit
49410fd49e
@ -0,0 +1,3 @@
|
|||||||
|
module datastructures
|
||||||
|
|
||||||
|
go 1.23.1
|
@ -0,0 +1,297 @@
|
|||||||
|
# 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
|
@ -0,0 +1,3 @@
|
|||||||
|
module debugger
|
||||||
|
|
||||||
|
go 1.23.1
|
@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func One() {
|
||||||
|
Two()
|
||||||
|
fmt.Println("One!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Two() {
|
||||||
|
Three()
|
||||||
|
fmt.Println("Two!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Three() {
|
||||||
|
fmt.Println("Three!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used in the simple example
|
||||||
|
// func main() {
|
||||||
|
// msg := "Hello debugger"
|
||||||
|
// One()
|
||||||
|
// fmt.Println(msg)
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Used for Goroutine debugging
|
||||||
|
|
||||||
|
func printMe(i int, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
fmt.Printf("I'm a go routine %d\n", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(9)
|
||||||
|
|
||||||
|
for i < 10 {
|
||||||
|
go func(rI int) {
|
||||||
|
printMe(rI, &wg)
|
||||||
|
}(i)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
Loading…
Reference in New Issue