Linux生成core文件及gdb调试
Linux下的C程序常常会因为内存访问错误等原因造成segment fault(段错误),此时如果系统core dump功能是打开的,那么将会有内存映像转储到硬盘上来,之后可以用gdb对core文件进行分析,还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。使用ulimit -a可以查看系统core文件的大小限制;使用ulimit -c [kbytes]可以设置系统允许生成的
Linux下的C程序常常会因为内存访问错误等原因造成segment fault(段错误),此时如果系统core dump功能是打开的,那么将会有内存映像转储到硬盘上来,之后可以用gdb对core文件进行分析,还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。
使用ulimit -a可以查看系统core文件的大小限制;使用ulimit -c [kbytes]可以设置系统允许生成的core文件大小,例如
- ulimit -c 0 不产生core文件
- ulimit -c 100 设置core文件最大为100k
- ulimit -c unlimited 不限制core文件大小
先看一段会造成段错误的程序:
- #include <stdio.h>
- int main()
- {
- char *ptr="linuxers.cn";
- *ptr=0;
- }
编译运行后结果如下:
- [leconte@localhost test]$ gcc -g -o test a.c
- [leconte@localhost test]$ ./test
- 段错误
此时并没有产生core文件,接下来使用ulimit -c设置core文件大小为无限制,再执行./test程序,结果如下:
- [leconte@localhost ~]$ ulimit -a
- core file size (blocks, -c) 0
- .........
- [leconte@localhost test]$ ulimit -c unlimited
- [leconte@localhost test]$ ulimit -a
- core file size (blocks, -c) unlimited
- ..............
- [leconte@localhost test]$ ./test
- 段错误 (core dumped)
- [leconte@localhost test]$ ls -al core.*
- -rw------- 1 leconte leconte 139264 01-06 22:31 core.2065
可见core文件已经生成,接下来可以用gdb分析,查看堆栈情况:
- [leconte@localhost test]$ gdb ./test core.2065
- GNU gdb Fedora (6.8-27.el5)
- Copyright (C) 2008 Free Software Foundation, Inc.
- License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
- 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 "i386-redhat-linux-gnu"...
- warning: exec file is newer than core file.
- warning: Can't read pathname for load map: Input/output error.
- Reading symbols from /lib/libc.so.6...done.
- Loaded symbols for /lib/libc.so.6
- Reading symbols from /lib/ld-linux.so.2...done.
- Loaded symbols for /lib/ld-linux.so.2
- Core was generated by `./test'.
- Program terminated with signal 11, Segmentation fault.
- [New process 2065]
- #0 0x0804836f in main () at a.c:6
- 6 *ptr=0;
从上述输出可以清楚的看到,段错误出现在a.c的第6行,问题已经清晰地定位到了。
很多系统默认的core文件大小都是0,我们可以通过在shell的启动脚本/etc/bashrc或者~/.bashrc等地方来加入 ulimit -c 命令来指定core文件大小,从而确保core文件能够生成。
除此之外,还可以在/proc/sys/kernel/core_pattern里设置core文件的文件名模板,详情请看core的官方man手册。
当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。你可以用GDB命令来查看当前的栈中的信息。
一、gdb 查看堆栈信息
下面是一些查看函数调用栈信息的GDB命令:
1、backtrace、bt
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
从上可以看出函数的调用栈信息:__libc_start_main --> main()--> func()
backtrace <n>
bt <n>
n是一个正整数,表示只打印栈顶上n层的栈信息。
backtrace <-n>
bt <-n>
-n表一个负整数,表示只打印栈底下n层的栈信息。
如果你要查看某一层的信息,你需要在切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。
2、frame、f
n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
查看当前栈层的信息,你可以用以下GDB命令:
frame 或 f
会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。
up
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令:
select-frame 对应于 frame 命令。
up-silently 对应于 up 命令。
down-silently 对应于 down 命令。
3、info frame、info f
(gdb) info f
Stack level 0, frame at 0xbffff5d4:
eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
called by frame at 0xbffff60c
source language c.
Arglist at 0xbffff5d4, args: n=250
Locals at 0xbffff5d4, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffff5d4, eip at 0xbffff5d8
4、info args
打印出当前函数的参数名及其值。
5、info locals
打印出当前函数中所有局部变量及其值。
6、info catch
打印出当前的函数中的异常处理信息。
例:
程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧)。要打印堆栈,发出命令 'bt'('backtrace' [回溯] 的缩写):
(gdb) bt
#0 0x80483ea in wib (no1=8, no2=8) at eg1.c:7
#1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21
此结果显示了在 main() 的第 21 行中调用了函数 wib()(只要使用 'list 21' 就能证实这一点),而且 wib() 在 0 号帧中,main() 在 1 号帧中。由于 wib() 在 0 号帧中,那么它就是执行程序时发生算术错误的函数。
实际上,发出 'info locals' 命令时,gdb 会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数(0 号帧)。可以使用命令 'frame' 打印当前帧。要查看 main 函数(在 1 号帧中)中的变量,可以发出 'frame 1' 切换到 1 号帧,然后发出 'info locals' 命令:
(gdb) frame 1
#1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21
21 result = wib(value, div);
(gdb) info locals
value = 8
div = 8
result = 4
i = 2
total = 6
此信息显示了在第三次执行 "for" 循环时(i 等于 2)发生了错误,此时 "value" 等于 "div"。可以通过如上所示在 'frame' 命令中明确指定号码,或者使用 'up' 命令在堆栈中上移以及 'down' 命令在堆栈中下移来切换帧。要获取有关帧的进一步信息,如它的地址和程序语言,可以使用命令 'info frame'。
gdb 堆栈命令可以在程序执行期间使用,也可以在 core 文件中使用,因此对于复杂的程序,可以在程序运行时跟踪它是如何转到函数的。
二、加载core文件
产生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中激发core dump的行.
gdb [exec file] [core file]
如:
gdb ./test test.core
加载后,可以发出 'info locals'、'print'、'info args' 和 'list' 等 gdb 命令或堆栈命令来查看调试信息。'info variables' 命令将打印出所有程序变量的值,但这要进行很长时间,因为 gdb 将打印 C 库和程序代码中的变量。
三、gdb连接到其它进程
除了调试 core 文件或程序之外,gdb 还可以连接到已经运行的进程(它的程序已经过编译,并加入了调试信息),并中断该进程。只需用希望 gdb 连接的进程标识替换 core 文件名就可以执行此操作。
以下是一个执行循环并睡眠的 示例程序:
eg2 示例代码
------------------------------------------------------------------------
- #include
- int main(int argc, char *argv[])
- {
- int i;
- for(i = 0; i < 60; i++)
- {
- sleep(1);
- }
- return 0;
- }
------------------------------------------------------------------------
1、编译并运行程序
使用 'gcc -g eg2.c -o eg2' 编译该程序并使用 './eg2 &' 运行该程序。请留意在启动该程序时在背景上打印的进程标识,在本例中是 1283:
------------------------------------------------------------------------
./eg2 &
[3] 1283
------------------------------------------------------------------------
2、发起连接
连接到进程: ‘ gdb [被调试文件] -c [进程号] ' ,也可以不要 -c。
启动 gdb 并指定进程标识,在我举的这个例子中是 'gdb eg2 1283'。gdb 会查找一个叫作 "1283" 的 core 文件。如果没有找到,那么只要进程 1283 正在运行(在本例中可能在 sleep() 中),gdb 就会连接并中断该进程:
------------------------------------------------------------------------
...
/home/seager/gdb/1283: No such file or directory.
Attaching to program: /home/seager/gdb/eg2, Pid 1283
...
0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
(gdb)
------------------------------------------------------------------------
3、gdb调试( gdb 命令或堆栈命令来查看调试信息)
此时,可以发出所有常用 gdb 命令。可以使用 'backtrace' 来查看当前位置与 main() 的相对关系,以及 mian() 的帧号是什么,然后切换到 main() 所在的帧,查看已经在 "for" 循环中运行了多少次:
------------------------------------------------------------------------
(gdb) backtrace
#0 0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
#1 0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:78
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
(gdb) frame 2
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
7 sleep(1);
(gdb) print i
$1 = 50
------------------------------------------------------------------------
4、调试完、断开连接等
detach / kill:输入' detach ' or 'kill',不需要进程号。
如果已经完成了对程序的修改,可以 'detach' 命令继续执行程序,或者 'kill' 命令杀死进程。
attach:先输入'file eg2',然后输入'attach 1283'
还可以首先使用 'file eg2' 装入文件,然后发出 'attach 1283' 命令连接到进程标识 1283 下的 eg2。
转载:http://leonzhan.iteye.com/blog/803008
转载:blog.csdn.net/woshikalz/article/details/41281513
更多推荐
所有评论(0)