软件调试实战入门(2)

根源的查找--源码调试及演示:

一)、代码的构建,以下代码实现非负整数的阶乘为例。

1//filename:factorial.c
2 #include <stdio.h>
3 #include <stdlib.h>
4  
5 int factorial(int n){
6     int result =1l;
7     if(n ==0){
8         return result;
9     }else{
10         result =  n * factorial(n -1);
11         return result;
12     }
13 }
14  
15 int main(int argc,char **argv)
16 {
17     int n;
18     int result;
19     if(argc !=2){
20         fprintf(stderr,"usage: factorial n, n >= 0\n");
21         return1;
22     }
23     n = atoi(argv[1]);
24     result = factorial(n);
25     printf("factorial%d =%d\n", n, result);
26  
27     return0;
28 }
该代码纯粹是为了演示之用:没有考虑到n的传入参数为负数时的特殊情况,也没有考虑int型数值的范围。

为了使程序与调试器一起运行,须要将编译器中的调试信息存放到程序代码中,这些调试信息又被称为调试符号或符号信息,它们之中包含着函数和变量的名称、CPU指令、源文件、甚至行号等源码间的关系。大多数编译器默认模式下未启用调试,代码中的调试信息会使程序变大,许多编译器优化方式在调试模式下是禁用的,程序运行会很慢。需要在编译时,使用-g将调试信息添加到程序代码中。

直接在vim中编译:":!gcc -g % -o %<"
直接在vim中调试:":!sh"转到shell提示符下,通过exit命令返回vim中

二)、使用GDB调试程序
(1)正确的参数:
gdb factorial
GNU gdb (GDB) 7.9
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from factorial...done.

以上灰色背景的都是gdb的提示信息
(gdb) r(run) 1
Starting program: /home/munication/factorial 1
factorial 1 = 1
[Inferior 1 (process 9122) exited normally]

(2)错误的参数:
(gdb) r -1
Starting program: /home/munication/factorial -1

Program received signal SIGSEGV, Segmentation fault.
0x00000000004005de in factorial (
   n=<error reading variable: Cannot access memory at address 0x7fffff7feffc>)
   at factorial.c:5
warning: Source file is more recent than executable.
5       int factorial(int n){

很明显出现了段错误,原因是传入的参数不正确,跟踪栈帧:
(gdb) bt
#0  0x00000000004005de in factorial (
   n=<error reading variable: Cannot access memory at address 0x7fffff7feffc>)
   at factorial.c:5
#1  0x0000000000400602 in factorial (n=-174662) at factorial.c:10
#2  0x0000000000400602 in factorial (n=-174661) at factorial.c:10
#3  0x0000000000400602 in factorial (n=-174660) at factorial.c:10
#4  0x0000000000400602 in factorial (n=-174659) at factorial.c:10
#5  0x0000000000400602 in factorial (n=-174658) at factorial.c:10
#6  0x0000000000400602 in factorial (n=-174657) at factorial.c:10
#7  0x0000000000400602 in factorial (n=-174656) at factorial.c:10
#8  0x0000000000400602 in factorial (n=-174655) at factorial.c:10
#9  0x0000000000400602 in factorial (n=-174654) at factorial.c:10
#10 0x0000000000400602 in factorial (n=-174653) at factorial.c:10
#11 0x0000000000400602 in factorial (n=-174652) at factorial.c:10
#12 0x0000000000400602 in factorial (n=-174651) at factorial.c:10
#13 0x0000000000400602 in factorial (n=-174650) at factorial.c:10
#14 0x0000000000400602 in factorial (n=-174649) at factorial.c:10
#15 0x0000000000400602 in factorial (n=-174648) at factorial.c:10
#16 0x0000000000400602 in factorial (n=-174647) at factorial.c:10
#17 0x0000000000400602 in factorial (n=-174646) at factorial.c:10
#18 0x0000000000400602 in factorial (n=-174645) at factorial.c:10
#19 0x0000000000400602 in factorial (n=-174644) at factorial.c:10
#20 0x0000000000400602 in factorial (n=-174643) at factorial.c:10
#21 0x0000000000400602 in factorial (n=-174642) at factorial.c:10
#22 0x0000000000400602 in factorial (n=-174641) at factorial.c:10
#23 0x0000000000400602 in factorial (n=-174640) at factorial.c:10
#24 0x0000000000400602 in factorial (n=-174639) at factorial.c:10
#25 0x0000000000400602 in factorial (n=-174638) at factorial.c:10
#26 0x0000000000400602 in factorial (n=-174637) at factorial.c:10
#27 0x0000000000400602 in factorial (n=-174636) at factorial.c:10
---Type <return> to continue, or q <return> to quit---
………………………………………………………………………………
#174649 0x0000000000400602 in factorial (n=-14) at factorial.c:10
#174650 0x0000000000400602 in factorial (n=-13) at factorial.c:10
#174651 0x0000000000400602 in factorial (n=-12) at factorial.c:10
#174652 0x0000000000400602 in factorial (n=-11) at factorial.c:10
#174653 0x0000000000400602 in factorial (n=-10) at factorial.c:10
#174654 0x0000000000400602 in factorial (n=-9) at factorial.c:10
#174655 0x0000000000400602 in factorial (n=-8) at factorial.c:10
#174656 0x0000000000400602 in factorial (n=-7) at factorial.c:10
#174657 0x0000000000400602 in factorial (n=-6) at factorial.c:10
---Type <return> to continue, or q <return> to quit---
#174658 0x0000000000400602 in factorial (n=-5) at factorial.c:10
#174659 0x0000000000400602 in factorial (n=-4) at factorial.c:10
#174660 0x0000000000400602 in factorial (n=-3) at factorial.c:10
#174661 0x0000000000400602 in factorial (n=-2) at factorial.c:10
#174662 0x0000000000400602 in factorial (n=-1) at factorial.c:10
#174663 0x000000000040066c in main (argc=2, argv=0x7fffffffde48) at factorial.c:24

不停的按回车键即可,一直到n= -1;

(3)不合适的参数:
(gdb) r 13
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/munication/factorial 13
factorial 13 = 1932053504
[Inferior 1 (process 9215) exited normally]

实际结果是:6,227,020,800
显示结果是:1,932,053,504
两结果插值:4,294,967,296 = 2^32

(4)使用断点控制代码的执行,查看变量和表达式的值
1)在main函数设置断点:
(gdb) b main
Breakpoint 1 at 0x400621: file factorial.c, line 19.
2)跟据行号设置断点:
(gdb) b 8
Breakpoint 2 at 0x4005ef: file factorial.c, line 8.
3)根据函数和行号设置断点:
(gdb) b factorial:11
Breakpoint 3 at 0x4005e1: file factorial.c, line 6.
(gdb) b 11
Breakpoint 3 at 0x4005e1: file factorial.c, line 11.
4)程序中导航并查看变量和表达式的值
设置参数13运行程序:
(gdb) start 13
Temporary breakpoint 5 at 0x400621: file factorial.c, line 19.
Starting program: /home/munication/factorial 13

Breakpoint 1, main (argc=2, argv=0x7fffffffde48) at factorial.c:19
19          if(argc != 2){
你就会发现程序在设置的第一个断点处停下来了。

(gdb) n
23          n = atoi(argv[1]);
输入n或next,程序以块为执行单位执行,将
if(argc != 2){
fprintf(stderr, "usage: factorial n, n >= 0\n");
return 1;
    }
代码块作为一条语句执行。如果继续使用n执行,将24行代码:result = factorial(n);中的函数调用直接调用完成,为了查看阶乘的计算过程,不能使用next命令。

(gdb) s
24          result = factorial(n);

(gdb) s

Breakpoint 3, factorial (n=13) at factorial.c:6
6           int result = 1;

此时可以看到已经进入到factorial函数中的第一条语句:int result = 1;

为了观察函数在参数为0时是不是能够实现返回1的能力,在第8行设置一个断点,为了观察函数参数大于0时的计算结果,在第11行设置一个断点。不但可以在开始设置断点,也可以随时设置断点。
使用命令c或continue是程序能够稍微快速执行:

(gdb) c
Continuing.

Breakpoint 3, factorial (n=12) at factorial.c:6
6           int result = 1l;

多执行几下c命令后:查看n变量的值,查看result的值
(gdb) p n
$13 = 11
(gdb) p result
$14 =
(gdb) p result
$6 =
39916800

(gdb) n

Breakpoint 4, factorial (n=12) at factorial.c:11
11              return result;

(gdb) p result
$15 = 479001600

此时可以看到当n为12时,执行的结果还是正确的,当n为13时

Breakpoint 4, factorial (n=13) at factorial.c:11
11              return result;
(gdb) p n
$16 = 13
(gdb) p result
$17 = 1932053504
此时是由于32位的整型数据的最大值为2^32 = 4,294,967,296,数据溢出后,剩余的数值,到此为止调试结束。
下表为gdb产用的调试命令:

命令名称调试命令 其他说明
运行程序 run [args]开始调试
启动程序 start [args]逐步调试
暂停 Ctrl-c 全部中断
继续运行 cont(c) 继续运行

step-over next(n) 逐过程,将函数作为一条语句,直接返回值即可
step-into step(s)逐语句,进入调用的函数,逐条语句执行
step-out finish(f) 跳出执行

断点 break(b) file:lineno按照文件行号设置断点,也可以以函数名设置
跟踪点 watch file: expr观察函数中的某一行
观察点 watch expr观察表达式的值

栈跟踪 bt, where堆栈跟踪
输出表达式 print expr输出表达式
显示表达式 display expr显示表达式

设置变量 set var var = expr设置变量值
设置环境变量 set env var[=val]设置属性、调试环境等

显示机器代码 Disassemble反汇编代码
在机器代码中执行step-over nexti逐过程
在机器代码中执行step-into stepi逐语句

条件断点 condition bnum根据条件设置断点
时间断点 handle, signal根据信号设置断点
异常断点 catch, throw测试异常
函数断点 break, function在函数所在处中断
临时断点 tbreak临时中断函数
列出所有断点 info breakpoints查看所有断点

将命令连接到断点 commands bnum将命令输出链接到断点
输出到命令行 printf将断点输出到命令行

查找函数 info functions expr查找函数断点
调用函数 call expr调用函数
修改函数返回值 return expr修改函数的返回数值

输出类型 What is arg转到声明
输出类型描述 ptype arg转到定义
输出内存内容 x arg临时输出内存的内容
选择栈帧 frame arg
输出栈帧描述 info frame调用堆栈


posted @ 2015-04-05 16:54  叕叒双又  阅读(389)  评论(0编辑  收藏  举报