gdb 调试
GDB是编译语言的源代码级调试器,主要是C和c++。
四个方面的功能:
1、按照自定义的方式启动运行需要调试的程序。
2、可以使用指定位置和条件表达式的方式来设置断点。
3、程序暂停时的值的监视。
4、动态改变程序的执行环境。
gdb 相关
-g 调试级别
需要用调试符号编译要调试的代码。
GCC为此提供了两个选项:-g
和-ggdb
。
后者添加特定于GDB的调试信息,而前者则为正在使用的任何目标操作系统以适当的格式生成信息,使其成为更具可移植性的选项。Linux,使用-g
还是-ggdb
没有什么区别。
两个选项都允许指定调试信息的级别,从0到3:
0:不产生任何调试信息,相当于省略-g
或-ggdb
开关。
1:这产生最少的信息,但包括函数名和外部变量,这足以产生回溯。
2:这是默认值,包含有关局部变量和行号的信息,以便您可以执行源代码级调试和单步执行代码。
3:这包括额外的信息,除其他事项外,意味着GDB可以正确处理宏扩展
-O 代码优化级别
编译器优化倾向于破坏源代码行和机器码行之间的关系,这使得遍历源代码变得不可预测。如果遇到这样的问题,很可能需要在不进行优化的情况下进行编译,省略-O
编译开关,或者使用-Og
,该开关启用不会干扰调试的优化。
gdb 使用
获取帮助命令 help
断点
break
命令用于设置断点,命令接受行号或者函数名作为参数。
break
也可设置条件断点:
如: break sayHello if count<1
使用info break
可以查看断点
使用 clear
可以清除断点
使用run
命令(缩写为 r)运行程序至断点
用next
命令(缩写为 n)单步执行程序
继续运行,可以使用continue
命令(缩写为 c )指导 gdb 继续运行程序,直至遇到下一个断点。
如果想继续单步执行,可以继续使用 next
,也是以使用 step
(缩写为 s), step
和 next
的最大的区别在于,step
遇到函数是会进入到内部,而next
不会进入内部。
监视变量
调试程序最基本的需求就是监视变量的值,可以使用 print 命令(缩写为 p) 显示指定变量的值。
如果要时刻监视某个变量的值,那么每次使用 print 就不方便。比较人性化的是,gdb 提供了watch 命令,用于设置另一种断点:“观察点”。
用法是: watch
变量名或表达式作为参数,一但值发生变化,就停下来。
临时修改变量
当某些特殊情况下,想让程序进入一些特殊的流程时,gdb允许用户在程序运行时改变变量的值,通过 set var 命令实现这一点。
查看堆栈情况
每次程序调用一个函数,函数的地址、参数、函数内部变量都会被压入“栈”(Stack) 中,运行时堆栈信息对于程序员非常重要,使用 “bt”命令可以看到当前运行时栈的情况。
退出 gdb
试完毕,使用quit
命令(缩写为q) 退出 gdb程序。
gdb的查看源码
可以用list命令来打印程序的源代码:
list <linenum>
:显示程序第linenum
行的周围的源程序。
list <function>
:显示函数名为function
的函数的源程序。
list
:显示当前行后面的源程序。
list
-:显示当前行前面的源程序。
可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数:
setlistsize <count>
:设置一次显示源代码的行数。
showlistsize
:查看当前listsize
的设置。
list命令还有下面的用法:
list <first>,
:显示从first行到last行之间的源代码。
list ,<last>
:显示从当前行到last行之间的源代码。
list +
:往后显示源代码。
coredump
Coredump 叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。
操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里,该文件也是二进制文件。
设置
在l inux 和 Solaris下是需要进行设置的。可以使用ulimit -a
来看这些设置
ulimit -c
可以设置core文件的大小,如果这个值为0
。则不会产生core文件,这个值太小,则core文件也不会产生,因为 core文件一般都比较大。
使用ulimit -c unlimited
来设置无限大,则任意情况下都会产生core文件。
缺省情况下,内核在coredump时所产生的core文件放在与该程序相同的目录中,并且文件名固定为core。很显然,如果有多个程序产生core文件,或者同一个程序多次崩溃,就会重复覆盖同一个core文件。
通过修改 kernel 的参数,可以指定内核所生成的 coredump 文件的文件名。例如,Easwy使 用下面的命令使 kernel 生成名字为 core_filename_time_pid 格式的 core dump 文件:
echo /corefile/core_%e_%t_%p > /proc/sys/kernel/core_pattern
echo
后面内容最好不要带上引号,有的系统会把引号也带入,这样,系统是不识别该内容的,也就会导致程序coredump而不会生成core文件。
可以在 core_pattern 模板中使用变量见下面的列表:
%%单个%字符
%p所dump进程的进程ID
%u所dump进程的实际用户ID
%g所dump进程的实际组ID
%s导致本次core dump的信号
%t core dump的时间 (由1970年1月1日计起的秒数)
%h主机名
%e程序文件名
产生 core dump 的可能原因
1.内存访问越界;
2.多线程程序使用了线程不安全的函数;
3.多线程读写的数据未加锁保护;
4.非法指针;
5.堆栈溢出。
如何调试
1)编译的时候添加-g选项,增加调试信息
2)gdb program core_file
bt或者where查看调用栈信息
如果你要查看某一层的信息,你需要切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。
frame <n>
f <n>
n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
up <n>
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down <n>
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令:
select-frame <n>
对应于 frame 命令。
up-silently <n>
对应于 up 命令。
down-silently <n>
对应于 down 命令。
查看当前栈层的信息,你可以用以下GDB命令:
frame 或 f
会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。
info frame
info f
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。
info args
打印出当前函数的参数名及其值。
info locals
打印出当前函数中所有局部变量及其值。
一些常用signal的含义
SIGABRT
:调用abort函数时产生此信号。进程异常终止。
SIGBUS
:指示一个实现定义的硬件故障。
SIGEMT
:指示一个实现定义的硬件故障。EMT这一名字来自PDP-11的emulator trap 指令。
SIGFPE
:此信号表示一个算术运算异常,例如除以0,浮点溢出等。
SIGKILL
:此信号指示进程已执行一条非法硬件指令。4.3BSD由abort函数产生此信号。SIGABRT现在被用于此。
SIGIOT
:这指示一个实现定义的硬件故障。IOT这个名字来自于PDP-11对于输入/输出TRAP(input/outputTRAP)指令的缩写。系统V的早期版本,由abort函数产生此信号。SIGABRT现在被用于此。
SIGQUIT
:当用户在终端上按退出键(一般采用Ctrl-/)时,产生此信号,并送至前台进程组中的所有进程。此信号不仅终止前台进程组(如SIGINT所做的那样),同时产生一个core文件。
SIGSEGV
:指示进程进行了一次无效的存储访问。名字SEGV表示“段违例(segmentationviolation)”。
SIGSYS
:指示一个无效的系统调用。由于某种未知原因,进程执行了一条系统调用指令,但其指示系统调用类型的参数却是无效的。
SIGTRAP
:指示一个实现定义的硬件故障。此信号名来自于PDP-11的TRAP指令。
SIGXCPU
:SVR4和4.3+BSD支持资源限制的概念。如果进程超过了其软CPU时间限制,则产生此信号。
SIGXFSZ
:如果进程超过了其软文件长度限制,则SVR4和4.3+BSD产生此信号。