[Linux] 第十章 调试
调试
*1 使用GDB和其他工具进行调试
*2 断言
*3 内存调试
@1,OS分配给程序的内存一般都会比程序实际需要使用的大一些。如果非法内存访问
出现在这部分内存区域内,硬件就可能检测不到。
如果想捕捉到数组访问方面的错误,最好增加数组元素的大小,因为这样同时也增加
错误的大小。如果只是在数组的结尾之后读取一个字节,我们很有可能看不到有错误
发生,因为分配给程序的内存大小会取整到OS的特定边界,一般分配给内存大小以8K
为单位递增。
gcc -Wall -pedantic -ansi 这些选项将启动许多警告和其他检查来检验程序是否符合
C 语言标准。
@2,程序的受控执行
-- 较复杂的调试器可以在源代码级别查看程序的比较详细的状态信息。GDB就可以!
-g 标志,它将使用特殊版本的C语言标准库以提供库函数中的调试支持。
调试信息的加入将使可执行程序的长度成倍增加。调完后,最好将调试信息从程序的
发行版本中删除。
你可以用命令 strip <file> 将可执行文件中的调试信息删除而不需要重新编译程序。 debug3.c
$ gcc -g -o debug3 debug3.c
$ gdb debug3
$ help
gdb 本身是一个基于文本的应用程序。gdb 直接按下回车键再次执行最近执行过的命令。
(gdb) run
Starting program: /home/hp/acm/debug3
Program received signal SIGSEGV, Segmentation fault.
0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
24 if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]);
(gdb) backtrace
#0 0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
#1 0x0000000000400738 in main () at debug3.c:30
(gdb) bt
#0 0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
#1 0x0000000000400738 in main () at debug3.c:30
(gdb) where
#0 0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
#1 0x0000000000400738 in main () at debug3.c:30
(gdb)
[因为在编译程序的时候添加了 gcc -g] 所以能够看到程序失败所停的位置。
backtrace 是桟跟踪,能够看到程序是怎么达到这里的!
这个程序比较简单,所以调试信息比较少。
gdb在停止程序时,给出的信息以及从跟踪桟得到的信息可以让我们看到函数参数的取值。
(gdb) print j
$1 = 4
(gdb) print p[3].id
$2 = 1
(gdb) print p[3]
$3 = {str = "efg", '\000' <repeats 8188 times>, id = 1}
(gdb) list
19 }
20 void bubble_sort (int len) {
21 int i, j;
22 for(i = 1; i < len; i++) { // len-1次就行
23 for(j = 0; j < len; j++) {
24 if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]);
25 }
26 }
27 }
28
有许多命令可以设置断点
(gdb) help breakpoint
(gdb) break 22
Breakpoint 1 at 0x40066d: file debug3.c, line 22.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/hp/acm/debug3
Breakpoint 1, bubble_sort (len=5) at debug3.c:22
22 for(i = 1; i < len; i++) { // len-1次就行
(gdb) print p[0]
$4 = {str = "abc", '\000' <repeats 8188 times>, id = 5}
(gdb) print p[0]@5
$5 = {{str = "abc", '\000' <repeats 8188 times>, id = 5}, {
str = "bcd", '\000' <repeats 8188 times>, id = 3}, {
str = "cde", '\000' <repeats 8188 times>, id = 2}, {
str = "def", '\000' <repeats 8188 times>, id = 4}, {
str = "efg", '\000' <repeats 8188 times>, id = 1}}
(gdb) cont
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
24 if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]);
(gdb) display p[0]@5
1: p[0]@5 = {{str = "bcd", '\000' <repeats 8188 times>, id = 3}, {
str = "cde", '\000' <repeats 8188 times>, id = 2}, {
str = "def", '\000' <repeats 8188 times>, id = 4}, {
str = "efg", '\000' <repeats 8188 times>, id = 1}, {
str = "abc", '\000' <repeats 8188 times>, id = 5}}
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>cont
>end
(gdb) cont
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb)
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
2: y p[0]@5
1: y p[0]@5
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040066d in bubble_sort
at debug3.c:22
breakpoint already hit 1 time
cont
2 breakpoint keep y 0x0000000000400679 in bubble_sort
at debug3.c:23
breakpoint already hit 1 time
深入学习 GDB (略)
断言
动态内存分配是很容易出现漏洞的领域,而且很难查找。
malloc / free
内存块通常是由malloc分配给指针变量的。
如果在一个已分配的内存块尾部的后面写数据,就很可能会损坏malloc库用于记录内存
分配情况的数据结构。出现这种情况后,经过一段时间,一个malloc调用,甚至是一个
free调用都会引发段错误并导致程序崩溃。
@2 valgrind 是一个工具,它有能力检测出许多问题。特别是它可以检测出数组访问错误
和内存泄露。http://valgrind.org 上找到它。它已被用在大型软件如KDE版本3的开发。
如果访问操作涉及一个已分配的内存块并且是非法的访问,valgrind将打印出消息。在
程序执行结束时,它将运行一个垃圾收集例程来检测是否有已分配的内存块未被释放。
如果有,它就会报告。
*1 使用GDB和其他工具进行调试
*2 断言
*3 内存调试
@1,OS分配给程序的内存一般都会比程序实际需要使用的大一些。如果非法内存访问
出现在这部分内存区域内,硬件就可能检测不到。
如果想捕捉到数组访问方面的错误,最好增加数组元素的大小,因为这样同时也增加
错误的大小。如果只是在数组的结尾之后读取一个字节,我们很有可能看不到有错误
发生,因为分配给程序的内存大小会取整到OS的特定边界,一般分配给内存大小以8K
为单位递增。
gcc -Wall -pedantic -ansi 这些选项将启动许多警告和其他检查来检验程序是否符合
C 语言标准。
@2,程序的受控执行
-- 较复杂的调试器可以在源代码级别查看程序的比较详细的状态信息。GDB就可以!
-g 标志,它将使用特殊版本的C语言标准库以提供库函数中的调试支持。
调试信息的加入将使可执行程序的长度成倍增加。调完后,最好将调试信息从程序的
发行版本中删除。
你可以用命令 strip <file> 将可执行文件中的调试信息删除而不需要重新编译程序。 debug3.c
#include <stdio.h> #include <stdlib.h> struct Node { char str[4096*2]; int id; }; struct Node p[5] = { {"abc", 5}, {"bcd", 3}, {"cde", 2}, {"def", 4}, {"efg", 1}, }; void swap(struct Node* a1, struct Node* a2) { struct Node temp = *a1; *a1 = *a2; *a2 = temp; } void bubble_sort (int len) { int i, j; for(i = 1; i < len; i++) { // len-1次就行 for(j = 0; j < len; j++) { if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]); } } } int main() { bubble_sort(5); int i; for(i = 0; i < 5; i++) { printf("%d\n", p[i].id); } return 0; }@3, 使用 gdb 进行调试
$ gcc -g -o debug3 debug3.c
$ gdb debug3
$ help
gdb 本身是一个基于文本的应用程序。gdb 直接按下回车键再次执行最近执行过的命令。
(gdb) run
Starting program: /home/hp/acm/debug3
Program received signal SIGSEGV, Segmentation fault.
0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
24 if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]);
(gdb) backtrace
#0 0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
#1 0x0000000000400738 in main () at debug3.c:30
(gdb) bt
#0 0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
#1 0x0000000000400738 in main () at debug3.c:30
(gdb) where
#0 0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
#1 0x0000000000400738 in main () at debug3.c:30
(gdb)
[因为在编译程序的时候添加了 gcc -g] 所以能够看到程序失败所停的位置。
backtrace 是桟跟踪,能够看到程序是怎么达到这里的!
这个程序比较简单,所以调试信息比较少。
gdb在停止程序时,给出的信息以及从跟踪桟得到的信息可以让我们看到函数参数的取值。
(gdb) print j
$1 = 4
(gdb) print p[3].id
$2 = 1
(gdb) print p[3]
$3 = {str = "efg", '\000' <repeats 8188 times>, id = 1}
(gdb) list
19 }
20 void bubble_sort (int len) {
21 int i, j;
22 for(i = 1; i < len; i++) { // len-1次就行
23 for(j = 0; j < len; j++) {
24 if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]);
25 }
26 }
27 }
28
有许多命令可以设置断点
(gdb) help breakpoint
(gdb) break 22
Breakpoint 1 at 0x40066d: file debug3.c, line 22.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/hp/acm/debug3
Breakpoint 1, bubble_sort (len=5) at debug3.c:22
22 for(i = 1; i < len; i++) { // len-1次就行
(gdb) print p[0]
$4 = {str = "abc", '\000' <repeats 8188 times>, id = 5}
(gdb) print p[0]@5
$5 = {{str = "abc", '\000' <repeats 8188 times>, id = 5}, {
str = "bcd", '\000' <repeats 8188 times>, id = 3}, {
str = "cde", '\000' <repeats 8188 times>, id = 2}, {
str = "def", '\000' <repeats 8188 times>, id = 4}, {
str = "efg", '\000' <repeats 8188 times>, id = 1}}
(gdb) cont
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00000000004006bc in bubble_sort (len=5) at debug3.c:24
24 if(p[j].id > p[j+1].id) swap(&p[j], &p[j+1]);
(gdb) display p[0]@5
1: p[0]@5 = {{str = "bcd", '\000' <repeats 8188 times>, id = 3}, {
str = "cde", '\000' <repeats 8188 times>, id = 2}, {
str = "def", '\000' <repeats 8188 times>, id = 4}, {
str = "efg", '\000' <repeats 8188 times>, id = 1}, {
str = "abc", '\000' <repeats 8188 times>, id = 5}}
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>cont
>end
(gdb) cont
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb)
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
2: y p[0]@5
1: y p[0]@5
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040066d in bubble_sort
at debug3.c:22
breakpoint already hit 1 time
cont
2 breakpoint keep y 0x0000000000400679 in bubble_sort
at debug3.c:23
breakpoint already hit 1 time
深入学习 GDB (略)
断言
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <assert.h> double my_sqrt(double x) { assert(x >= 0.0); return sqrt(x); } int main() { my_sqrt(2.0); printf("%lf\n", my_sqrt(2.0)); exit(0); }*3 内存调试
动态内存分配是很容易出现漏洞的领域,而且很难查找。
malloc / free
内存块通常是由malloc分配给指针变量的。
如果在一个已分配的内存块尾部的后面写数据,就很可能会损坏malloc库用于记录内存
分配情况的数据结构。出现这种情况后,经过一段时间,一个malloc调用,甚至是一个
free调用都会引发段错误并导致程序崩溃。
@2 valgrind 是一个工具,它有能力检测出许多问题。特别是它可以检测出数组访问错误
和内存泄露。http://valgrind.org 上找到它。它已被用在大型软件如KDE版本3的开发。
如果访问操作涉及一个已分配的内存块并且是非法的访问,valgrind将打印出消息。在
程序执行结束时,它将运行一个垃圾收集例程来检测是否有已分配的内存块未被释放。
如果有,它就会报告。