Author:gnuhpc
WebSite:blog.csdn.net/gnuhpc
1.调试技术的几个准则
2.Linux下代码调试工具
主要使用的GDB,以及基于GDB的图形化工具,如DDD或eclipse,选择上看个人习惯了。
命令行式的GDB启动较快,可以在ssh终端下使用,操作简洁,并且在调试GUI程序时不会崩溃,但较之图形化则在单步调试或设置断点时非常不方便。
当然你可以使用Vim等编辑器的插件或者补丁(clewn or vimGDB)来弥补这一缺憾,并且在GDB6.1以上的版本你可以使用GDB -tui这个模式(或者在GDB的命令行模式下按CTRL-x-a)打开一个类似于图形界面的文本界面模式,在这个界面中你可以使用上下键查看源代码(CTRL-P 和 CTRL-N完成输入过的命令的查看).
或者你还可以使用cGDB这个工具(很庆幸这个项目在停止了三年后又有人开始维护了),这个工具是将GDB用curses包装了一下,提供了一些很好用的feature(Esc和i键在代码和命令框间切换;在代码框中支持vim型的操作;在命令框中支持tab键补全命令;在移动到想加入断点的行(行号为高亮白色)直接用空格键,设定好后行号会变红;)。另外,在调试C-S程序时推荐使用eclipse。
在本文中,重点介绍ddd的操作,因为这个工具即结合了GDB命令行和图形界面的操作。其余请参阅各个工具的手册。
3.GDB命令行最基本操作
实例:插入排序算法调试
用伪代码描述这个过程如下:
拟调试代码如下:
//
// insertion sort,
//
// usage: insert_sort num1 num2 num3 ..., where the numi are the numbers to
// be sorted
int x[10], // input arrayy[10], // workspace array
num_inputs, // length of input array
num_y = 0; // current number of elements in y
void get_args(int ac, char **av){ int i;
num_inputs = ac - 1;for (i = 0; i < num_inputs; i++)
x[i] = atoi(av[i+1]);}void scoot_over(int jj){ int k;
for (k = num_y-1; k > jj; k++)
y[k] = y[k-1];}void insert(int new_y){ int j;
if (num_y = 0) { // y empty so far, easy casey[0] = new_y;return;
}// need to insert just before the first y
// element that new_y is less than
for (j = 0; j < num_y; j++) {
if (new_y < y[j]) {
// shift y[j], y[j+1],... rightward
// before inserting new_y
scoot_over(j);y[j] = new_y;return;
}}}void process_data()
{for (num_y = 0; num_y < num_inputs; num_y++)
// insert new y in the proper place
// among y[0],...,y[num_y-1]
insert(x[num_y]);}void print_results()
{ int i;
for (i = 0; i < num_inputs; i++)
printf("%d/n",y[i]);
}int main(int argc, char ** argv){ get_args(argc,argv);process_data();print_results();}
我们编译一下:
gcc -g -Wall -o insert_sort ins.c
注意我们要使用-g选项告诉编译器在可执行文件中保存符号表——我们程序中变量和代码对应的内存地址。
现在我们开始运行一下,我们使用“从小处开始准则”,首先使用两个数进行测试:
./insert_sort 12 5
我们发现该程序没有退出,貌似进入了一个死循环。我们开始使用ddd调试这个程序:
ddd insert_sort
运行程序,传入两个参数:
r 12 5
此时程序一直运行不退出,按Ctrl+C暂停程序的执行
(GDB) r 12 5
^C
Program received signal SIGINT, Interrupt.
0x080484ff in insert (new_y=3) at insert_sort.c:45
/home/gnuhpc/MyCode/Debug/Chapter_01/insert_sort/pg_019/insert_sort.c:45:939:beg:0x80484ff
(GDB)
我们可以看到程序停止在第49行。我们看一下num_y现在的值:
(GDB) p num_y
$1 = 1
这里的$1指的是你要GDB告诉你的第一个变量。找到了这个地方后,我们看看在num_y=1时都发生了什么,我们在insert函数(第27行)设置断点(你也可以直接使用break insert在这个函数的入口设置断点),并且设置GDB在断点1处(你可以通过info break命令查看断点)只当num_y==1时才停止:
(GDB) b 27
Breakpoint 1 at 0x80484a1: file insert_sort.c, line 27.
(GDB) condition 1 num_y==1
(GDB)
上述命令也可以使用break if合一:
(GDB) break 27 if num_y==1
然后再运行程序,随后用n单步调试发现我们跳到了该函数的出口处:
此时我们看看num_y的值,以便查看到底这个for循环执行的情况。
(GDB) p num_y
$2 = 0
此时的情况是我们进入这个函数时num_y为1,但是现在num_y为0,在中间这个变量被改变了。现在你知道Bug就在30-36行间。同时,通过单步调试你发现31-33行被跳过了,34、35行为注释,那么Bug就在第30或第36行间了。
我们现在仔细看这两行就能得出结论了:30行有个典型的if判断条件写成赋值的错误,致命的是这个变量是全局变量,直接导致49行的for循环变量一直被重置。我们修改后重新编译(可以另开一个编辑器,不用退出ddd),然后再运行
(GDB) r 12 5
5
0
虽然没有了死循环,但是结果还是不对的。
请注意,初始的时候数组y是空的,在#49进行第一次循环时,y[0]应该为12,在第二个循环中,程序应该挪动12为5腾出位置插入,但是此时这个结果看上去是5取代了12。
此时单步调试进入for循环,#37看y[0]的值,的确是12。我们执行到scoot_over函数时,根据自顶向下准则我们单步跳过,继续执行到#41,看看结果对错再决定是不是要单步进入scoot_over函数:
我们发现12根本就没有被移动,说明scoot_over函数有问题,我们去掉insert函数入口的断点,在scoot_over入口处设置断点,当num_y=1的时候终止:b scoot_over if num_y==1。进一步单步调试后发现这个#23的for循环就没有执行。
(GDB) p jj
$12 = 0
(GDB) p k
$13 = 0
我们看到是因为没有满足for循环条件而不能进入循环。在这里12应该从y[0]移动到y[1],那么我们确定是循环的初始化错误,应该为k = num_y,将这个地方修改后编译运行,程序出现段错误。我们清空所有的断点,然后在
(GDB) r
Program received signal SIGSEGV, Segmentation fault.
0x08048483 in scoot_over (jj=0) at insert_sort.c:24
(GDB)
这里指出在24行出现seg fault,那么要么k超过了数组界限,要么k-1为负的。打印一下k的值,我们就发现:
(GDB) p k
$14 = 992
(GDB)
远远超过k应该有的值。查看num_y 为1,说明在处理第二个要排序的数时出错,再打印jj值,发现为0,就发现我们的for循环k++应该改为k—。
编译运行,发现ok。但是运行多个数据就又出错了:
(GDB) r 12 5 19 22 6 1
1
5
6
12
0
0Program exited with code 06.
(GDB)
我们看到结果中从19开始的排序都有问题,我们在for (j = 0; j < num_y; j++) 这一行行设置断点,条件为new_y==19的时候:
(GDB) break 36 if new_y==19
Breakpoint 10 at 0x80484b1: file insert_sort.c, line 36.
(GDB)
单步调试就发现我们没有对当要插入的元素大于所有元素时进行处理。在#44后加入y[num_y] = new_y;重新编译,运行程序正确,至此,我们通过一个简单的例子演示了一下如何使用GDB进行调试。
参考文献:
《Art of Debugging》
《Linux® Debugging and Performance Tuning: Tips and Techniques》
Author:gnuhpc
WebSite:blog.csdn.net/gnuhpc