一、debug和realease版本的区别
区别
debug是给程序员用的版本,添加了调试信息,用于解决软件或程序中出现的问题,realease是发行给客户使用的版本,并未添加调试信息,只需要给客户提供优越的产品使用环境即可,至于是否能够debug调试,这是程序员才应该关心的主要问题。
可以看到debug版本的字节大小是要大于realease版本的,这是因为其中添加了调试信息。
通过指令可以读取可执行程序的二进制构成,显示出具体的二进制软件的内部所形成的特定格式。
值得注意的是:每一个二进制程序不仅仅只是一堆二进制代码,他们内部都是有特定格式的,Linux中形成的可执行程序是elf格式。
readelf命令
readelf命令是一个可以读取ELF(Executable and Linkable Format)文件头部信息的Linux命令。ELF文件是一种可执行文件和共享库的标准格式,包含了代码段、数据段、符号表和动态链接信息等。通过readelf命令,用户可以查看这些信息,如ELF文件的类型、入口地址、节区信息、符号表、重定位信息、动态链接信息等。
常用的readelf命令选项包括:
- -a或--all:显示所有信息,默认选项;
- -h或--file-header:显示文件头(包括ELF文件类型、入口地址等);
- -S或--sections:显示节区信息;
- -s或--symbols:显示符号表信息;
- -r或--relocs:显示重定位信息;
- -d或--dynamic:显示动态链接信息。
下面就是debug中所添加的调试信息,而realease版本是没有这些调试信息的
二、 调试器gdb
3.1gdb介绍
gdb是GNU开源组织发布的一个用于Unix/Linux的程序调试工具。
与其它调试器一样,gdb可以在程序中设置断点、查看变量值,跟踪程序执行的过程。
利用调试器的这些功能可以方便地找出程序中存在的非语法错误。
3.2环境配置
安装
编写一个用于测试的程序test.c
#include <stdio.h>int get_sum(int n)
{int sum = 0, i;for (i = 1; i <= n; i++)sum += i;return sum;
}int main()
{int j = 100, res;res = get_sum(j);printf("1+2+...+%d = %d\n", j, res);return 0;
}
编译并运行该程序
gcc -g test.c -o test
需要-g参数
./test
几点注意:
- 默认情况下,gdb无法进行对gcc现在发布的程序进行调试,因为gcc默认生成的软件是realease版本的,没有调试信息,无法被调试器gdb调试,并且gcc是默认动态链接的,如果想静态链接还需要加static选项,所有gcc是有两种默认行为的。
- gcc编译时添加-g选项可以使得gcc发布的程序软件为debug版本,这样就可以通过gdb对程序进行调试
3.3gdb的启动和退出
启动:
1. gdb [程序名]
gdb test
2. gdb --quiet
(gdb)file test
退出
(gdb)quit
3.4gdb常用命令
help命令
其他命令
显示程序源代码list
list: 输出从上次调用list命令开始向后的10行程序代码
list -: 输出从上次调用list命令处向前10行代码
list n: 输出n行附近的10行代码
list [函数名]: 输出函数附近的10行代码
list n1,n2: 显示第n1行到n2行的代码
搜索字符串
forward/search [字符串]: 从当前位置向后查找指定的字符串所在的程序行,查找时不包括当前行,可以用list n,n将当前行设置为n
reverse-search [字符串]: 从当前行向前查找第一个匹配的字符串
执行程序
在shell环境下使用gdb test,或在gdb环境下使用file test只是载入了程序,但是程序是没有运行的
运行:
(gdb)run
3.5断点的设置和管理
设置断点
1 以行数设置断点
格式: break n
功能: 当程序运行到指定行时,会暂停执行,指定行的代码不执行
例如:
(gdb)break 15
(gdb)run
2 以函数设置断点
格式: break [函数名]
例如:
(gdb)break get_sum
(gdb)run
3 以条件表达式设置断点
格式: break [行号或函数名] if [条件]
功能: 程序在运行过程中,满足设定条件时,程序在所设置处中断
例如:
(gdb)break 7 if i==99
含义: 当程序执行到第7行时,判断条件i==99是否成立,若成立则中断
4 以条件表达式变化设置断点
格式: watch [条件表达式]
功能: 程序在运行过程中,当满足设定条件时,程序中断
注意:watch必须在程序运行的过程中设置观察点,即运行run之后,并且要保证条件表达式中的变量已经使用过。
例1
(gdb)break 13
(gdb)run
(gdb)watch sum==3
例2
(gdb)break 5
(gdb)run
(gdb)watch sum==3
查看断点
查看当前设置的断点
格式: info breakpoints( info b)
例如:
(gdb)break 7
(gdb)break 15 if res==5050
(gdb)info breakpoints
管理断点
1. 使中断失效或有效
失效: disable [断点编号]
有效: enable [断点编号]
2. 删除断点
clear [行号]: 删除此行的断点
delete [断点编号]: 删除指定编号的断点, 若有一次删除多个断点,各断点编号以空格分开。
delete: 删除程序中所有的断点
3. 取消断点 d + 断点编号
查看和设置变量的值
当程序执行到中断点暂停时,往往需要查看变量或表达式的值,借此了解程序的执行状态,进而发现问题。
1 print
功能: 打印变量或表达式的值,还可以用来对某个变量进行赋值。
print [变量或表达式]: 打印变量或表达式的值
print [变量]=[值]: 对变量进行赋值
例如:
(gdb)break 7
(gdb)run
(gdb)print i < n
(gdb)print i
(gdb)print sum
(gdb)print i=200
(gdb)continue
2 whatis
功能: 用于显示某个变量或表达式的数据类型
格式: whatis [变量或表达式]
例如:
(gdb)break 7
(gdb)run
(gdb)whatis i
(gdb)whatis sum
(gdb)whatis sum+0.5
3 set
功能: 给变量赋值
格式: set variable [变量]=[值]
4.临时查看变量或地址的值:p + 变量或地址
控制程序的执行
当程序执行到指定的中断点时,完成相关的debug操作后,可以让程序继续运行
1 continue
程序继续运行,直到下一个断点或运行完毕(运行至下一个断点处:c(continue))
2 kill
结束当前程序的调试
3 next/step
功能: 一次一条执行程序代码(逐过程:n(next),逐过程:n(next))
区别: next把函数调用当做一条语句来执行;step追踪进入函数,一次一条地执行内部代码。
4.跳转到指定行:until+行号
可以利用until来跳出循环