人啊,总要不断地与记忆作斗争。
1, 加-g参数编译可执行性文件, 如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。
cc -g test.c -o test
静态检查程序错误,splint test.c
单元测试可使用CUnit, gcc -o test.o -I /usr/local/include/CUnit -L /usr/local/lib/ -g testcase.c -l cunit ./test.o
2, 启动gdb, gdb test
启动GDB的方法有以下几种:
a、gdb
program也就是你的执行文件,一般在当然目录下。
b、gdb
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
如果产生coredump呢? 需要执ulimit -c unlimited才可以(缺省是coredump文件大小是0字节所以它不生成)。
并不是所有的异常都会生成coredump文件,附录1是一个运行异常时会产生coredump文件的例子。
运行后coredump文件产生在当前文件夹的core文件中。分析它: gdb ./test ./core, 如下显示了是哪一句出了错误:
(gdb) bt
#0 0x000000000040054d in core_dump () at test.c:5
#1 0x0000000000400585 in main () at test.c:10
c、gdb
通过sudo gdb启动gdb后直接attach
3, 一些常用gdb命令
l, 列出源代码
break 16, 第16行设置断点
b fn1 if a>b, 条件设置断点
break func, 在函数的入口处设置断点
delete/disable/enable/clear 16, 删除第16个断点
delete breakpoints,清除所有断点
info break, 查看断点
r, 运行程序,到断点处处时会停止运行等待用户输入(run), 在gdb中正常结束的程序也可以用此命令重新运行
n, 单步执行(next)
s, 进入函数(step)
c, 继续运行(continue), 到下一个断点处
p
set var i=1, 修改变量的值
info registers, 查看寄存器
bt, 查看函数堆栈
call 函数(参数),调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
display 表达式:在单步运行时将非常有用,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。如: display a
watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如:watch a
q, 退出
4, 使用GDB调试多进程程序, https://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/
5, 性能分析, 比如哪个函数调用了多少次,被谁调用了, 平均每次调用花费多少时间等。这时候要用gprof,gprof是分析profile输出的。
要想执行时输出profile文件编译时要加-pg选项, gcc -o helloworld.o -pg -g helloworld.c
执行上面语句后会在当前目录下生成gmon.out文件, 然后用gprof去读取并显示出来,gprof -b -A -p -q helloworld.o gmon.out >prof_info.txt
附录1, 产生coredump文件的例子
#include
int core_dump() {
int i;
for (i = 5; i >= 0; i--) {
printf("(%d, %d)\n", i, 100 / i);
}
return 0;
}
int main() {
core_dump();
return 0;
}