# Using the Debugger Go doesn't ship with a debugger but you can use the delve package instead. https://www.youtube.com/watch?v=UA0SirX6Siw ``` 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