Learn to Use GDB for Code Debugging

Learn to Use GDB for Code Debugging
Introduction: Use the GNU Debugger to solve your code issues.                                       
This article contains 6312 words, estimated reading time: 8 minutes
https://linux.cn/article-13203-1.htmlAuthor: Seth KenlonTranslator: Xingyu.Wang

The GNU Debugger, commonly referred to by its command gdb, is an interactive console that helps you browse source code and analyze what is happening during execution. Essentially, it is a reverse engineering tool for problems that occur in malfunctioning applications.

Troubleshooting can be complicated. The GNU Debugger is not a particularly complex application, but if you don’t know where to start or even when and why you might need to turn to GDB for troubleshooting, it can be overwhelming. If you have been using print, echo, or printf statements to debug your code and are starting to wonder if there is something more powerful, then this tutorial is for you.

Learn to Use GDB for Code Debugging

Code with Errors

To start using GDB, you need some code. Here is a sample application written in C++ (it’s okay if you don’t usually program in C++, the principles are the same across all languages), sourced from an example in the Guessing Game series.

#include <iostream>
#include <stdlib.h> //srand
#include <stdio.h>  //printf

using namespace std;

int main () {

srand (time(NULL));
int alpha = rand() % 8;
cout << "Hello world." << endl;
int beta = 2;

printf("alpha is set to is %s\n", alpha);
printf("kiwi is set to is %s\n", beta);

 return 0;
} // main

This code example contains a bug, but it does compile (at least with GCC 5). If you are familiar with C++, you may have already spotted it, but it is a simple issue that can help new GDB users understand the debugging process. Compile and run it to see the error:

$ g++ -o buggy example.cpp
$ ./buggy
Hello world.
Segmentation fault

Learn to Use GDB for Code Debugging

Excluding Segmentation Faults

From this output, you can infer that the setting of the variable alpha is correct, because otherwise you wouldn’t see the line of code that comes after it execute. Of course, this is not always true, but it is a good working theory, and if you use printf as logging and debugging, you would basically come to the same conclusion. From here, you can assume the bug is in the line after the successfully printed line. However, it is unclear whether the error is in the next line or several lines later.

The GNU Debugger is an interactive troubleshooting tool, so you can use the gdb command to run the buggy code. For better results, you should recompile your buggy application from the source code with debug symbols. First, see what information GDB can provide without recompiling:

$ gdb ./buggy
Reading symbols from ./buggy...done.
(gdb) start
Temporary breakpoint 1 at 0x400a44
Starting program: /home/seth/demo/buggy

Temporary breakpoint 1, 0x0000000000400a44 in main ()
(gdb)

When you start GDB with a binary executable as an argument, GDB loads the application and waits for your instructions. Since this is your first time running GDB on this executable, it makes sense to try to reproduce the error in hopes that GDB can provide further insight. Intuitively, the command GDB uses to start the loaded application is start. By default, GDB has a built-in breakpoint, so when it encounters the main function of your application, it pauses execution. To let GDB continue execution, use the command continue:

(gdb) continue
Continuing.
Hello world.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6
(gdb)

No surprise: the application crashed shortly after printing “Hello world,” but GDB can provide the function call that was happening when the crash occurred. This may be enough for you to find the bug causing the crash, but to better understand GDB’s capabilities and the general debugging process, imagine if the issue is still unclear and you want to dig deeper into what is happening in the code.

Learn to Use GDB for Code Debugging

Compile Code with Debug Symbols

To make the most of GDB, you need to compile your executable file with debug symbols. You can use the -g option in GCC to generate these symbols:

$ g++ -g -o debuggy example.cpp
$ ./debuggy
Hello world.
Segmentation fault

Compiling debug symbols into an executable results in a much larger file, so they are typically not distributed for convenience. However, if you are debugging open-source code, it makes sense to recompile tests with debug symbols:

$ ls -l *buggy* *cpp
-rw-r--r--    310 Feb 19 08:30 debug.cpp
-rwxr-xr-x  11624 Feb 19 10:27 buggy*
-rwxr-xr-x  22952 Feb 19 10:53 debuggy*

Learn to Use GDB for Code Debugging

Debugging with GDB

Load the new executable file (in this case, debuggy) to start GDB:

$ gdb ./debuggy
Reading symbols from ./debuggy...done.
(gdb) start
Temporary breakpoint 1 at 0x400a44
Starting program: /home/seth/demo/debuggy

Temporary breakpoint 1, 0x0000000000400a44 in main ()
(gdb)

As mentioned earlier, use the start command:

(gdb) start
Temporary breakpoint 1 at 0x400a48: file debug.cpp, line 9.
Starting program: /home/sek/demo/debuggy

Temporary breakpoint 1, main () at debug.cpp:9
9       srand (time(NULL));
(gdb)

This time, the automatic main breakpoint can indicate the line number where GDB paused and the code contained in that line. You can use continue to resume normal operation, but you already know the application will crash before it completes, so you can use the next keyword to step through your code line by line:

(gdb) next
10  int alpha = rand() % 8;
(gdb) next
11  cout << "Hello world." << endl;
(gdb) next
Hello world.
12  int beta = 2;
(gdb) next
14      printf("alpha is set to is %s\n", alpha);
(gdb) next

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6
(gdb)

From this process, it can be confirmed that the crash did not occur when setting the beta variable, but rather when executing the printf line. This bug has been exposed several times in this article (culprit: providing the wrong data type to printf), but for now, let’s assume the solution is still unclear and needs further investigation.

Learn to Use GDB for Code Debugging

Setting Breakpoints

Once your code is loaded into GDB, you can query GDB for the data produced by the code so far. To try data introspection, restart your application by issuing the start command again, and proceed to line 11. A quick way to reach line 11 is to set a breakpoint at that specific line number:

(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Temporary breakpoint 2 at 0x400a48: file debug.cpp, line 9.
Starting program: /home/sek/demo/debuggy

Temporary breakpoint 2, main () at debug.cpp:9
9       srand (time(NULL));
(gdb) break 11
Breakpoint 3 at 0x400a74: file debug.cpp, line 11.

After setting the breakpoint, continue execution with continue:

(gdb) continue
Continuing.

Breakpoint 3, main () at debug.cpp:11
11      cout << "Hello world." << endl;
(gdb)

Now paused at line 11, just after the alpha variable is set and before beta is set.

Learn to Use GDB for Code Debugging

Using GDB for Variable Introspection

To view the value of a variable, use the print command. In this sample code, the value of alpha is random, so your actual result may differ from mine:

(gdb) print alpha
$1 = 3
(gdb)

Of course, you cannot see the value of a variable that has not yet been established:

(gdb) print beta
$2 = 0

Learn to Use GDB for Code Debugging

Using Control Flow

To continue, you can step through the code to reach the point where beta is set to a value:

(gdb) next
Hello world.
12  int beta = 2;
(gdb) next
14  printf("alpha is set to is %s\n", alpha);
(gdb) print beta
$3 = 2

Additionally, you can set a watchpoint, which is like a breakpoint and a way to control the flow of execution in GDB. In this case, you know that the beta variable should be set to 2, so you can set a watchpoint to notify you when beta‘s value changes:

(gdb) watch beta > 0
Hardware watchpoint 5: beta > 0
(gdb) continue
Continuing.

Breakpoint 3, main () at debug.cpp:11
11      cout << "Hello world." << endl;
(gdb) continue
Continuing.
Hello world.

Hardware watchpoint 5: beta > 0

Old value = false
New value = true
main () at debug.cpp:14
14      printf("alpha is set to is %s\n", alpha);
(gdb)

You can manually step through the execution of the code with next, or you can control the execution of the code with breakpoints, watchpoints, and catchpoints.

Learn to Use GDB for Code Debugging

Using GDB to Analyze Data

You can view data in different formats. For example, view the value of beta in octal:

(gdb) print /o beta
$4 = 02

To see its address in memory:

(gdb) print /o &beta
$5 = 0x2

You can also see the data type of a variable:

(gdb) whatis beta
type = int

Learn to Use GDB for Code Debugging

Using GDB to Solve Errors

This introspection not only helps you better understand what code is executing but also how it is executing. In this example, running the whatis command on the variable gave you a clue that both your alpha and beta variables are integers, which might jog your memory about the printf syntax, making you realize that in your printf statement, you must use %d instead of %s. Making this change allows the application to run as expected, with no more obvious errors present.

It is especially frustrating to discover bugs when the code compiles, but the trickiest bugs are often like this; if they were easy to spot, they wouldn’t be bugs. Using GDB is one way to hunt them down and eliminate them.

Learn to Use GDB for Code Debugging

Download Our Cheat Sheet

The truth of life is that even the most basic programming can have bugs. Not all errors lead to applications failing to run (or even compile), and not all errors are caused by incorrect code. Sometimes bugs occur intermittently based on a particularly creative combination of choices made by a user. Sometimes programmers inherit bugs from libraries they use in their own code. Whatever the reason, bugs are essentially everywhere, and it is the programmer’s job to discover and eliminate them.

The GNU Debugger is a useful tool for finding bugs. There is much more you can do with it than I have demonstrated in this article. You can read about its many features through the GNU Info reader:

$ info gdb

Whether you are just starting to learn GDB or are a professional, it is helpful to remind yourself of what commands are available and what the syntax for those commands is.

◈ Download GDB Cheat Sheet

via: https://opensource.com/article/21/3/debug-code-gdb

Author: Seth Kenlon, Topic: lujun9972, Translator: wxy, Proofreader: wxy

This article is originally compiled by LCTT and honorably launched by Linux China.

Learn to Use GDB for Code Debugging
Welcome to reprint according to CC-BY-NC-SA agreement,
If you need to reprint, please leave a message under the article “Reprint: Public Account Name“,
We will add you to the whitelist, authorizing “to modify when reprinting articles“.

Leave a Comment