Core Dump 定位
打开CoreDump 功能
- 在终端中输入命令 ulimit -c ,输出的结果为 0,说明默认是关闭 core dump 的,即当程序异常终止时,也不会生成 core dump 文件。
- 我们可以使用命令 ulimit -c unlimited 来开启 core dump 功能,并且不限制 core dump 文件的大小; 如果需要限制文件的大小,将 unlimited 改成你想生成 core 文件最大的大小,注意单位为 blocks(KB)。
- 用上面命令只会对当前的终端环境有效,如果想需要永久生效,可以修改文件 /etc/security/limits.conf
文件,关于此文件的设置参看 这里 。增加一行:
# /etc/security/limits.conf
#
#Each line describes a limit for a user in the form:
#
# -
* soft core unlimited
修改 core 文件保存的路径
默认生成的 core 文件保存在可执行文件所在的目录下,文件名就为 core。
通过修改 /proc/sys/kernel/core_uses_pid 文件可以让生成 core 文件名是否自动加上 pid 号。
例如 echo 1 > /proc/sys/kernel/core_uses_pid,生成的 core 文件名将会变成 core.pid,其中 pid 表示该进程的 PID。
还可以通过修改 /proc/sys/kernel/core_pattern来控制生成 core 文件保存的位置以及文件名格式。例如可以用 echo “/tmp/corefile-%e-%p-%t” > /proc/sys/kernel/core_pattern 设置生成的 core 文件保存在 “/tmp/corefile” 目录下,文件名格式为 “core-命令名-pid-时间戳”。**这里** 有更多详细的说明!
实用GDB 定位Core
- 用gcc -g 编译生成带调试信息文件:
gcc -g demo.c -o demo
- 运行程序,产生core 文件:
./demo
- 实用GDB 调试:
gdb demo core
实际例子如下
#include
#include
#include
// void demoFun( char *start,... )
// {
// va_list arg_ptr;
// char *value =NULL;
// char *valueOne=NULL;
// value = start;
// va_start(arg_ptr, start );
// do{
// printf("value :%s len=%d \n", value,strlen(value) );
// valueOne = value;
// value=va_arg(arg_ptr, int);
// }while( value!=NULL);
// va_end(arg_ptr);
// printf( "%s %d value=%s\n",__FUNCTION__,__LINE__,valueOne );
// return;
// }
void printfData( int *pData )
{
*pData = 100;
}
void main( void )
{
int *null_ptr = NULL;
printfData( null_ptr );
return ;
}
运行步骤如下
alex@alex-virtual-machine:/mnt/hgfs/gagent/working/demo$ gcc -g demo.c -o demo
alex@alex-virtual-machine:/mnt/hgfs/gagent/working/demo$ ./demo
Segmentation fault (core dumped)
alex@alex-virtual-machine:/mnt/hgfs/gagent/working/demo$ gdb demo core
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
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 "i686-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /mnt/hgfs/gagent/working/demo/demo...done.
warning: core file may not match specified executable file.
[New LWP 8867]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./demo.c'.
Program terminated with signal 11, Segmentation fault.
#0 0x080483ba in printfData (pData=0x0) at demo.c:24
24 *pData = 100;
(gdb) where #----------------->显示在哪个函数发生错误,以及在哪个地方调用了该函数
#0 0x080483ba in printfData (pData=0x0) at demo.c:24
#1 0x080483da in main () at demo.c:29
(gdb) quit #----------------->退出gdb
GDB 断点(BreakPoint):
在代码的指定位置中断,这个是我们用得最多的一种。设置断点的命令是break,它通常有如下方式:
break <function> 在进入指定函数时停住
break <linenum> 在指定行号停住。
break +/-offset 在当前行号的前面或后面的offset行停住。offiset为自然数。
break filename:linenum 在源文件filename的linenum行处停住。
break ... if <condition> ...可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置break if i=100,表示当i为100时停住程序。
可以通过info breakpoints [n]命令查看当前断点信息。此外,还有如下几个配套的常用命令:
delete 删除所有断点
delete breakpoint [n] 删除某个断点
disable breakpoint [n] 禁用某个断点
enable breakpoint [n] 使能某个断点
观察点(WatchPoint):
在变量读、写或变化时中断,这类方式常用来定位bug。
watch <expr> 变量发生变化时中断
rwatch <expr> 变量被读时中断
awatch <expr> 变量值被读或被写时中断
可以通过info watchpoints [n]命令查看当前观察点信息
捕捉点(CatchPoint):
捕捉点用来补捉程序运行时的一些事件。如:载入共享库(动态链接库)、C++的异常等。通常也是用来定位bug。
捕捉点的命令格式是:catch
throw C++抛出的异常时中断
catch C++捕捉到的异常时中断
exec 调用系统调用exec时(只在某些操作系统下有用)
fork 调用系统调用fork时(只在某些操作系统下有用)
vfork 调用系统调用vfork时(只在某些操作系统下有用)
load 或 load <libname> 载入共享库时(只在某些操作系统下有用)
unload 或 unload <libname> 卸载共享库时(只在某些操作系统下有用)
另外,还有一个tcatch
捕捉点信息的查看方式和代码断点的命令是一样的,这里就不多介绍了。
在特定线程中中断
你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB很容易帮你完成这一工作。
break <linespec> thread <threadno>
break <linespec> thread <threadno> if ...
linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过"info threads"命令来查看正在运行程序中的线程信息。如果你不指定thread <threadno>
则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如:
(gdb) break frik.c:13 thread 28 if bartab > lim
当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。
恢复程序运行和单步调试
在gdb中,和调试步进相关的命令主要有如下几条:
continue 继续运行程序直到下一个断点(类似于VS里的F5)
next 逐过程步进,不会进入子函数(类似VS里的F10)
setp 逐语句步进,会进入子函数(类似VS里的F11)
until 运行至当前语句块结束
finish 运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)
PS:这些命令大部分可以简写为第一个字母,在日常使用过程中,往往只会输入第一个字符即可执行该命令,我标红的即是通常的使用方式。这几条命令使用非常频繁,并且可以带一些附加参数以实现高级功能,需要熟练掌握。
Addr2Line
Addr2line 工具(它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。这种功能对于将跟踪地址转换成更有意义的内容来说简直是太棒了。
实战一
#include
int main()
{
int *p = NULL;
*p = 0;
printf("bad\n");
return 0;
}
alex@ubuntu:/media/psf/Home/myself/C$ gcc -g addr2line.c -o demo
alex@ubuntu:/media/psf/Home/myself/C$ ./demo
Segmentation fault
alex@ubuntu:/media/psf/Home/myself/C$ dmesg |grep demo
[ 2555.121891] demo[10553]: segfault at 0 ip 080483e9 sp bf9a7f60 error 6 in demo[8048000+1000]
alex@ubuntu:/media/psf/Home/myself/C$ addr2line
addr2line.c bit.c csv/ DS1302/ .DS_Store hello.c log/ printflog/
a.out crc/ demo DS1302.rar gdb_test list/ mac/ test.c
alex@ubuntu:/media/psf/Home/myself/C$ addr2line -e demo 080483e9
/media/psf/Home/myself/C/addr2line.c:6
解释:
gcc -o demo -g addr2line.c生成带有调试信息的可执行文件 demodmesg | grep demo:获得执行demo后的出错信息, 你可以将结果理解为日志, 其中的080483c9是一个地址, 正是这个地址出错了addr2line -e demo 080483e9:将出错地址转换成源代码对应的行, 结果为6, 也就是说源代码第6行有问题。 一看, 果然是,万恶的*p=0;被揪出来了。