Debugging by using GDB
During the development process of any application, developers will always need to perform some kind of code debugging. PHP, Python, and most of the other dynamic languages, are able to be modified at runtime, as long as the modifications do not explicitly need to be compiled. We can easily print data in dynamic operating environments, outputting our changes and printing variable information directly. In Go, you can of course speckle your code with Println
s before-hand to display variable information for debugging purposes, but any changes to your code need to be recompiled every time. This can quickly become cumbersome. If you've programmed in Python or Javascript, you'll know that the former provides tools such as pdb and ipdb for debugging, and the latter has similar tools that are able to dynamically display variable information and facilitate single-step debugging. Fortunately, Go has native support for a similar tool which provides such debugging features: GDB. This section serves as a brief introduction into debugging Go applications using GDB.
GDB debugging profile
GDB is a powerful debugging tool targeting UNIX-like systems, released by the FSF (Free Software Foundation). GDB allows us to do the following things:
Initial settings can be customize according to the specific requirements of your application.
Can be set so that the program being debugged in the developer's console stops at the prescribed breakpoints (breakpoints can be conditional expressions).
When the program has been stopped, you can check its current state to see what happened.
Dynamically change the current program's execution environment.
To debug your Go applications using GDB, the version of GDB you use must be greater than 7.1.
When compiling Go programs, the following points require particular attention:
Using
-ldflags "-s"
will prevent the standard debugging information from being printedUsing
-gcflags "-N-l"
will prevent Go from performing some of its automated optimizations -optimizations of aggregate variables, functions, etc. These optimizations can make it very difficult for GDB to do its job, so it's best to disable them at compile time using these flags.
Some of GDB's most commonly used commands are as follows:
list
Also used in its abbreviated form l
, list
is used to display the source code. By default, it displays ten lines of code and you can specify the line you wish to display. For example, the command list 15
displays ten lines of code centered around line 15, as shown below.
break
Also used in its abbreviated form b
, break
is used to set breakpoints, and takes as an argument that defines which point to set the breakpoint at. For example, b 10
sets a break point at the tenth row.
delete
Also used in its abbreviated form d
, delete
is used to delete break points. The break point is set followed by the serial number. The serial number can be obtained through the info breakpoints
command. Break points set with their corresponding serial numbers are displayed as follows to set a break point number.
backtrace
Abbreviated as bt
, this command is used to print the execution of the code, for instance:
info
The info
command can be used in conjunction with several parameters to display information. The following parameters are commonly used:
info locals
Displays the currently executing program's variable values
info breakpoints
Displays a list of currently set breakpoints
info goroutines
Displays the current list of running goroutines, as shown in the following code, with the *
indicating the current execution
1 running runtime.gosched
2 syscall runtime.entersyscall
3 waiting runtime.gosched
4 runnable runtime.gosched
print
Abbreviated as p
, this command is used to print variables or other information. It takes as arguments the variable names to be printed and of course, there are some very useful functions such as $len() and $cap() that can be used to return the length or capacity of the current strings, slices or maps.
whatis
whatis
is used to display the current variable type, followed by the variable name. For instance, whatis msg
, will output the following:
type = struct string
next
Abbreviated as n
, next
is used in single-step debugging to skip to the next step. When there is a break point, you can enter n
to jump to the next step to continue
continue
Abbreviated as c
, continue
is used to jump out of the current break point and can be followed by a parameter N, which specifies the number of times to skip the break point
set variable
This command is used to change the value of a variable in the process. It can be used like so: set variable <var> = <value>
The debugging process
Now, let's take a look at the following code to see how GDB is typically used to debug Go programs:
Now we compile the file, creating an executable file called "gdbfile":
Use the GDB command to start debugging :
After first starting GDB, you'll have to enter the run
command to see your program running. You will then see the program output the following; executing the program directly from the command line will output exactly the same thing:
Ok, now that we know how to get the program up and running, let's take a look at setting breakpoints:
In the above example, we use the b 23
command to set a break point on line 23 of our code, then enter run
to start the program. When our program stops at our breakpoint, we typically need to look at the corresponding source code context. Entering the list
command into our GDB session, we can see the five lines of code preceding our breakpoint:
Now that GDB is running the current program environment, we have access to some useful debugging information that we can print out. To see the corresponding variable types and values, type info locals
:
To let the program continue its execution until the next breakpoint, enter the c
command:
After each c
, the code will execute once then jump to the next iteration of the for
loop. It will, of course, continue to print out the appropriate information.
Let's say that you need to change the context variables in the current execution environment, skip the process then continue to the next step. You can do so by first using info locals
to get the variable states, then the set variable
command to modify them:
Finally, while running, the program creates a number of goroutines. We can see what each goroutine is doing using info goroutines
:
From the goroutines
command, we can have a better picture of what Go's runtime system is doing internally; the calling sequence for each function is plainly displayed.
Summary
Links
Last updated
Was this helpful?