Primary image for Introduction to Go Debugging with GDB

Introduction to Go Debugging with GDB

I spent the vast majority of my time in the last 4 years writing, reading and debugging Python or JavaScript code. The process of learning Go was like a beautiful hike in the mountains with a small rock in my shoe. A lot of things impressed me, but using println to debug my code was travelling too far into the past. In Python we have pdb/@ipdb@ to debug the code while running it, JavaScript offers similar tools. Over the years this pattern became a very important part of my development workflow.

Today I realized that Go has builtin support for the Gnu debugger.

For the sake of this article we are going to use the simple program below:

package main

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;time&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">counting</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">c</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">i</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nb">close</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&quot;Starting main&quot;</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">bus</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&quot;starting a gofunc&quot;</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="nx">counting</span><span class="p">(</span><span class="nx">bus</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">count</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">bus</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;count:&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">count</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

To use <span class="caps">GDB</span> you need to compile your program with the options -gcflags "-N -l". These options prevent the compiler from using inline functions and variables.

go build -gcflags "-N -l" gdbsandbox.go
Here is an example of an interactive debugging GDB session: yml@simba$ gdb gdbsandbox GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04 Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <> First we run our program: (gdb) run Starting program: /home/yml/Developments/go/src/gdbsandbox/gdbsandbox Starting main count: 0 count: 1 count: 2 [...] count: 9 [Inferior 1 (process 13507) exited normally] Now that we know how to run our program we probably want to set a breakpoint: (gdb) help break Set breakpoint at specified line or function. break [LOCATION] [thread THREADNUM] [if CONDITION] LOCATION may be a line number, function name, or "*" and an address. [...] (gdb) break 22 Breakpoint 1 at 0x400d7a: file /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go, line 22. (gdb) run Starting program: /home/yml/Developments/go/src/gdbsandbox/gdbsandbox Starting main [New LWP 13672] [Switching to LWP 13672] Breakpoint 1, main.main () at /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go:22 22 for count := range bus { (gdb) Once GDB stops at your breakpoint you view the context: (gdb) help list List specified function or line. With no argument, lists ten more lines after or around previous listing. "list -" lists the ten lines before a previous ten-line listing. [...] (gdb) list 17 msg := "Starting main" 18 fmt.Println(msg) 19 bus := make(chan int) 20 msg = "starting a gofunc" 21 go counting(bus) 22 for count := range bus { 23 fmt.Println("count:", count) 24 } 25 } You can also inspect the variables: (gdb) help print Print value of expression EXP. Variables accessible are those of the lexical environment of the selected stack frame, plus all those whose scope is global or an entire file. [...] (gdb) print msg $1 = "starting a gofunc" Earlier in the code we started a goroutine. I want to introspect this part of my program next time we execute the line 10. (gdb) break 10 Breakpoint 3 at 0x400c28: file /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go, line 10. (gdb) help continue Continue program being debugged, after signal or breakpoint. If proceeding from breakpoint, a number N may be used as an argument, which means to set the ignore count of that breakpoint to N - 1 (so that the breakpoint won't break until the Nth time it is reached). [...] (gdb) continue Continuing. The last thing we are going to demo today is how to change the value of a variable at runtime. Breakpoint 3, main.counting (c=0xf840001a50) at /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go:10 10 time.Sleep(2 * time.Second) (gdb) help whatis Print data type of expression EXP. Only one level of typedefs is unrolled. See also "ptype". (gdb) whatis count type = int (gdb) print count $3 = 1 (gdb) set variable count=3 (gdb) print count $4 = 3 (gdb) c Continuing. count: 3 We only covered the following commands:
  • list
  • next
  • print
  • continue
  • break <line number>
  • whatis
  • set variable <var>=<value>
…and this barely scratches the surface of what you can do with GDB, here are some links if you want to learn more:
Yann Malet

About the author

Yann Malet

Yann builds and architects performant digital platforms for publishers. In 2015, Yann co-authored High-Performance Django with Peter Baumgartner. Prior to his involvement with Lincoln Loop, Yann focused on Product Lifecycle Management systems (PLM) for several large …