CGDB: A More Convenient Debugging Tool Than GDB

Table of Contents
  • Example Code with Bugs

  • GDB Debugging Operations

  • CGDB Debugging Operations

Learning from Others’ Experiences!

CGDB is a frontend for GDB, providing a graphical interface in the terminal for debugging code (based on ncurse), which is very convenient. Compared to GDB, it can significantly improve efficiency.

This article will share the most basic usage of CGDB. If this is your first time hearing about it, I strongly recommend you give it a try; you will definitely love it!

Example Code with Bugs

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

typedef struct USER_DATA{
	char data[32];
	unsigned short data_len;
	unsigned int flag;
}__attribute((packed));

const unsigned char * g_data =  "hello";

/*
Function: Load a piece of data
Parameter 1: data[OUT]: Buffer to load data
Parameter 2: len [OUT]: Actual length of loaded data
Return value: 0-success, else-failure
*/
static int get_data(unsigned char *data, unsigned int *len)
{
	assert(data && len);
	memcpy((void *)data, (void *)g_data, strlen(g_data));
	*len = strlen(g_data);
	return 0;
}

int main(int argc, char *argv[])
{
    // Create struct variable
	struct USER_DATA user_data;
	user_data.flag = 0xA5;
	
	// Load data into struct variable
	if (0 == get_data(user_data.data, &user_data.data_len))
	{
		printf("get_data ok! \n");
		printf("data_len = %d, data = %s \n", user_data.data_len, user_data.data);
		printf("user_data.flag = 0x%x \n", user_data.flag);  // Expected value: 0xA5
	}
	else
	{
		printf("get_data failed! \n");
	}
	return 0;
}

Before compiling, take a look at the code. Can you find the bug in it?

Of course, during compilation, the compiler gives a warning about the risk. Since the example code is simple, it is easy to spot.

However, in a project, if you don’t like to eliminate compilation Warning, this bug can be quite hidden.

Compile the test code: gcc -g test.c -o test

Since you need to use GDB for debugging, don’t forget to add the -g option.

GDB Debugging Operations

$ gdb ./test
(gdb) r   // Execute once at full speed
(gdb) r
Starting program: /home/captain/demos_2022/cgdb/test 
test start... 
get_data ok! 
data_len = 5, data = hello 
user_data.flag = 0x0 
[Inferior 1 (process 9933) exited normally]

Noticing that the value of user_data.flag is incorrect, I decided to set a breakpoint at the line before calling get_data and start execution from the beginning:

Check the line number of the code:

(gdb) l main
18		*len = strlen(g_data);
19		return 0;
20	}
21	
22	int main(int argc, char *argv[])
23	{
24		struct USER_DATA user_data;
25		user_data.flag = 0xA5;
26		if (0 == get_data(user_data.data, &user_data.data_len))
27		{

Set a breakpoint at line 25:

(gdb) b 25
Breakpoint 1 at 0x400771: file test.c, line 25.

Start running:

(gdb) r
Starting program: /home/captain/demos_2022/cgdb/test 

Breakpoint 1, main (argc=1, argv=0x7fffffffdc58) at test.c:25
25		user_data.flag = 0xA5;

The execution stopped at the breakpoint, and the assignment statement has not been executed yet, so let’s execute it step by step:

(gdb) step
26		if (0 == get_data(user_data.data, &user_data.data_len))

At this point, print the value and address of the variable user_data.flag:

Since we will enter the called function soon, this variable will become invisible, so we need to print it by address.

(gdb) print &user_data.flag
$1 = (unsigned int *) 0x7fffffffdb62
(gdb) print/x user_data.flag
$2 = 0xa5

At this point, the assignment is correct. Continue to execute, entering the called function get_data(),

(gdb) step
get_data (data=0x7fffffffdb40 "n\333\377\377\377\177", len=0x7fffffffdb60) at test.c:16
16		assert(data && len);

This function has only 4 lines of code, and we execute each line step by step, printing the content of user_data.flag variable each time.

Step to the next line at memcpy and check if the content at the address of user_data.flag is still: 0xa5:

(gdb) step
17		memcpy((void *)data, (void *)g_data, strlen(g_data));
(gdb) print/x *0x7fffffffdb62
$3 = 0xa5

Continue stepping (since we don’t need to follow the internals of memcpy and strlen, use the next command) and print:

(gdb) next
18		*len = strlen(g_data);     // This line is about to be executed
(gdb) print/x *0x7fffffffdb62
$4 = 0xa5
(gdb) next
19		return 0;
(gdb) print/x *0x7fffffffdb62
$5 = 0x0

Problem discovered: After executing the statement *len = strlen(g_data), the content in the address of the variable user_data.flag has changed.

After carefully checking the code, we can diagnose that the data type used was incorrect.

Bug Fix: The last parameter of the get_data() function should indeed be a pointer of type unsigned short.

The problem is solved, but looking back at the gdb debugging process, it is still quite cumbersome: debugging instructions and code display mixed together, requiring many commands to be typed.

CGDB Debugging Operations

After starting CGDB, the terminal window is divided into two parts: the upper part is the code window, and the lower part is the debugging window.

CGDB: A More Convenient Debugging Tool Than GDB

Press the ESC key to enter the code window, where you can browse the code up and down, and perform a series of operations:

Spacebar: Set or cancel a breakpoint;

o: View the file where the code is located;

/ or ?: Search for strings in the code;

There are many convenient hotkeys:

-: Shrink the code window;

+: Expand the code window;

gg: Move the cursor to the top of the file;

GG: Move the cursor to the bottom of the file;

ctrl + b: Scroll up one page in the code;

ctrl + u: Scroll up half a page in the code;

ctrl + f: Scroll down one page in the code;

ctrl + d: Scroll down half a page in the code;

Press the i key to return to the debugging window, entering debugging mode, where the debugging commands are almost the same as GDB!

In other words: You can perform debugging operations while viewing the code in real-time, greatly improving efficiency.

Let’s go through the debugging process of GDB again:

Press the ESC key to enter the code window; if the line number in front of the code is white, it indicates the current line.

Press the j key to move the highlighted current line down. When moving to line 25, as shown below:

CGDB: A More Convenient Debugging Tool Than GDB

Press the spacebar to set a breakpoint at this line, and the line number turns red:

CGDB: A More Convenient Debugging Tool Than GDB

And the debugging window prints a line of information:

(gdb) 
Breakpoint 1 at 0x400771: file test.c, line 25.

Press the i key to return to the debugging operation window, then input the run command r, and it will stop at line 25, as shown by the green arrow:

CGDB: A More Convenient Debugging Tool Than GDB

Of course, the debugging window will also print related information:

(gdb) r
Starting program: /home/captain/demos_2022/cgdb/test 

Breakpoint 1, main (argc=1, argv=0x7fffffffdc58) at test.c:25

Step step through this assignment statement, and then print the value and address of user_data.flag:

(gdb) print/x user_data.flag
1: /x user_data.flag = 0xa5
(gdb) print &user_data.flag
2: &user_data.flag = (unsigned int *) 0x7fffffffdb62

At this point, the assignment statement executed correctly, and the printed value also meets expectations.

Then execute the step command, entering the function get_data():

(gdb) step
get_data (data=0x7fffffffdb40 "n\333\377\377\377\177", len=0x7fffffffdb60) at test.c:16

At this point, the upper code window automatically enters the related code of get_data(), as shown below:

CGDB: A More Convenient Debugging Tool Than GDB

Continue stepping, and before executing the assignment statement *len = strlen(g_data);, print the content in the address of the variable user_data.flag:

(gdb) print/x *0x7fffffffdb62
$2 = 0xa5

Correct! Then execute the assignment statement, and print again:

(gdb) next
(gdb) print/x *0x7fffffffdb62
$3 = 0x0

Problem discovered: After executing the statement *len = strlen(g_data), the content in the address of the variable user_data.flag has changed.

Summary:

The operation process of CGDB, although I wrote it quite verbose, is actually very smoother to use, just like chocolate!

– EOF –

CGDB: A More Convenient Debugging Tool Than GDB

Add the WeChat of the homepage manager, not just C/C++ skills +1

CGDB: A More Convenient Debugging Tool Than GDBCGDB: A More Convenient Debugging Tool Than GDB

The homepage manager will also regularly share C/C++ development learning resources and selected technical articles on personal WeChat, and will share some interesting activities, job referrals, and how to use technology for hobby projects from time to time.

CGDB: A More Convenient Debugging Tool Than GDB

Add WeChat, open a window

Recommended Reads Click the title to jump

1. GDB Debugging – From Beginner Practice to Principles

2. Understand in One Article | GDB Underlying Implementation Principles

3. C++ Successor? Overview of Carbon Syntax!

Follow ‘CPP Developers’

Read selected C/C++ technical articles

Likes and views are the biggest support❤️

Leave a Comment