gdb调试 与 coredump调试
- 1. 启动gdb
- 2.gdb中的相关命令
- 3. coredump调试(附属于gdb调试中一种,当程序出现错误时,会使用coredump调试)
- 1)coredump是什么?
- 2)前期设置
- 3)什么情况下会导致程序异常退出
- 4)如何调试
- 4.gdb调试-直接打印堆栈信息
1. 启动gdb
- 注意:
如果系统没有安装gdb ,可以体验使用源码安装的方式来安装:- wget http://ftp.gnu.org/gnu/gdb/gdb-8.1.1.tar.gz
- tar -zxvf gdb-8.1.1.tar.gz
- cd gdb-8.1.1
- ./configure
- make
- make install
如何判断自己的虚拟机gdb安装成功?
要判断您的虚拟机上的GDB是否已成功安装,可以按照以下步骤进行操作:
1)打开终端并运行gdb命令。您可以通过在终端中输入“gdb”并按Enter键来执行此操作。如果GDB已正确安装,则应该看到一些有关GDB版本和版权信息的输出。否则,您可能需要重新安装GDB或查看安装日志以查找错误。
2)检查GDB的版本。您可以使用“gdb --version”命令来检查GDB的版本。如果GDB已正确安装,则应该看到有关版本号和版权信息的输出。
3)在GDB中运行测试程序。您可以从终端中运行GDB,并使用GDB来调试一个简单的测试程序,例如“Hello World”程序。如果您能够成功启动程序并在GDB中设置断点和调试步骤,则说明您的GDB已正确安装并可以正常使用。
eg:使用gdb来调试hello.c#include<stdio.h> int main(){ printf("Hello World!\n"); return 0; }
wxn@WXNNB:~/projects/test$ gcc -g hello.c -o hello
wxn@WXNNB:~/projects/test$ gdb hello
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
Copyright (C) 2014 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 "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello...done.
(gdb) break main
Breakpoint 1 at 0x400531: file hello.c, line 3.
(gdb) run
Starting program: /home/wxn/projects/test/hello
Breakpoint 1, main () at hello.c:3
3 printf("Hello World!\n");
(gdb) continue
Continuing.
Hello World!
[Inferior 1 (process 56541) exited normally]
(gdb) next
The program is not being run.
(gdb) quit
在上述示例中,我们首先使用gcc编译hello.c并生成可执行文件hello,然后使用gdb启动可执行文件。接着我们使用“break main”命令在主函数入口处设置断点,并使用“run”命令启动程序。程序执行到断点处时暂停执行,并且我们可以使用“next”命令逐行执行程序,还可以使用“print”命令查看变量的值。我们还使用“continue”命令继续执行程序。最后,我们使用“quit”命令退出gdb。
gdb调试命令中,continue与next的区别?
区别在于continue命令会一直运行程序直到遇到断点或程序结束,而next命令会逐行执行程序并将控制权移动到下一行。
- 使用gdb的前提:编译时加上-g参数
$ gcc -g hello.c -o hello
- 启动gdb 调试:
$ gdb hello
2.gdb中的相关命令
- 设置断点
- 在某一行设置断点。
break 10
- 在某个函数的入口处设置断点。
break main
- 条件断点。
break 10 if i == 10
- 临时断点。
您可以使用“tbreak”命令设置临时断点,以便在程序执行到该断点时暂停,但一旦程序继续执行到下一个断点或程序结束时,该断点就会自动删除。例如,如果要在源代码的第20行上设置临时断点,则可以使用以下命令:
tbreak 20
- 使用info break 可以查看断点
- 使用 clear 可以清除断点
- 查看断点信息
info break
- 使用 clear 可以清除断点
clear + 断点行号或已经设置断点的函数名称
- 运行程序
run
- 单步执行
- 如果想继续运行,可以使用==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程序。
3. coredump调试(附属于gdb调试中一种,当程序出现错误时,会使用coredump调试)
1)coredump是什么?
程序异常退出时,会产生一个core文件,该文件记录了程序运行时的内存,
寄存器状态,堆栈指针,内存管理信息还有各种函数调用堆栈信息等,我们
可以理解为是程序工作当前状态存储生成的一个文件,通过工具分析这个文
件,我们可以定位到程序异常退出的时候对应的堆栈调用等信息,找出问题
所在并进行及时解决。
2)前期设置
- 设置core文件生成的目录,其中%e表示程序文件名,%p表示进程ID,
否则会在程序的当前目录生成dore文件;
echo /data/coredump/core.%e.%p >/proc/sys/kernel/core_pattern
cat /proc/sys/kernel/core_pattern
注意:根目录(/)
在执行以上命令时,要先确保/data/coredump/目录存在,
可以执行ls -la /data/coredump/查看,如果没有请创建
- 当前执行程序的用户对core目录有写权限且有足够的空间存储core文件;
直接sudo su,在root权限下执行,然后给个666权限
cd /data
sudo chmod 666 coredump/
- 生成不受限制的core文件;
ulimit -c unlimited
然后使用 ulimit -a 再去查看core文件大小限制
3)什么情况下会导致程序异常退出
非法指针的访问,堆栈溢出 , 访问越界
4)如何调试
来一个访问空指针的例子:
hello1.c
#include <stdio.h>
int fun(int *p)
{
int y = *p;
return y;
}
int main(){
int *p = NULL;
return fun(p);
}
- 编译的时候添加-g选项,增加调试信息
这里再强调一下,必须是root权限!!!
gcc hello1.c -g -o hello1
- 执行
./hello1
再使用ls -la /data/coredump/查看一下core文件是否生成在我们配置的/data/coredump/文件夹下:
红框圈住的一条信息就是我们刚才生成的!!!
同时date命令也可以查看当前时间,查看一下时间是否和上面新生的core文件时间接近
- gdb program core_file
-program :可执行文件
-core_file :新生成的core信息
对应到我的虚拟机是:
gdb hello1 /data/coredump/core.hello1.65606
frame <n>
f <n>
n是一个从0开始的整数,是栈中的层编号。
比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
info frame info f 这个命令会打印出更为详细的当前栈层的信息, 只不过,大多数都是运行时的内内地址。 比如:函数地址,调用函数的地址,被调用函数的地址, 目前的函数是由什么样的程序语言写成的、函数参数地址及值、 局部变量的地址等等。如:
info args
打印出当前函数的参数名及其值。
info locals
打印出当前函数中所有局部变量及其值。
所有的调试信息:
root@WXNNB:/home/wxn/projects/test# gdb hello1 /data/coredump/core.hello1.65606
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
Copyright (C) 2014 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 "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello1...done.
[New LWP 65606]
Core was generated by `./hello1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00000000004004f9 in fun (p=0x0) at hello1.c:4
4 int y = *p;
(gdb) bt
#0 0x00000000004004f9 in fun (p=0x0) at hello1.c:4
#1 0x000000000040051f in main () at hello1.c:9
(gdb) where
#0 0x00000000004004f9 in fun (p=0x0) at hello1.c:4
#1 0x000000000040051f in main () at hello1.c:9
(gdb) frame 0
#0 0x00000000004004f9 in fun (p=0x0) at hello1.c:4
4 int y = *p;
(gdb) frame 1
#1 0x000000000040051f in main () at hello1.c:9
9 return fun(p);
(gdb) info f
Stack level 1, frame at 0x7fff09d7eb20:
rip = 0x40051f in main (hello1.c:9); saved rip = 0x7f5d96837f45
caller of frame at 0x7fff09d7eb00
source language c.
Arglist at 0x7fff09d7eb10, args:
Locals at 0x7fff09d7eb10, Previous frame's sp is 0x7fff09d7eb20
Saved registers:
rbp at 0x7fff09d7eb10, rip at 0x7fff09d7eb18
(gdb) info args
No arguments.
(gdb) info locals
p = 0x0
4.gdb调试-直接打印堆栈信息
#include <stdio.h>
#include <signal.h>
//信号钩子函数,获取栈信息,然后在日志中打印
void handle_segv(int signum)
{
void *array[100];//指针数组
size_t size;//大小
char **strings;//二级指针
size_t i;//计数
signal(signum, SIG_DFL); /* 还原默认的信号处理handler */
size = backtrace (array, 100);
strings = (char **)backtrace_symbols (array, size);
fprintf(stderr,"Launcher received SIG: %d Stack trace:\n", signum);
for (i = 0; i < size; i++)
{
fprintf(stderr,"%d %s \n",i,strings[i]);
}
free (strings);
}
int fun(int *p)
{
int y = *p;
return y;
}
int main(){
int *p = NULL;
signal(SIGSEGV, handle_segv); // SIGSEGV 11 Core Invalid memory reference
signal(SIGABRT, handle_segv); // SIGABRT 6 Core Abort signal from
return fun(p);
}
root@WXNNB:/home/wxn/projects/test# vim hello2.c
root@WXNNB:/home/wxn/projects/test# gcc -g hello2.c -o hello2
hello2.c: In function ‘handle_segv’:
hello2.c:14:15: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
strings = (char **)backtrace_symbols (array, size);
^
hello2.c:19:9: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘size_t’ [-Wformat=]
fprintf(stderr,"%d %s \n",i,strings[i]);
^
hello2.c:22:5: warning: incompatible implicit declaration of built-in function ‘free’ [enabled by default]
free (strings);
^
root@WXNNB:/home/wxn/projects/test# ./hello2
Launcher received SIG: 11 Stack trace:
0 ./hello2() [0x4006e9]
1 /lib/x86_64-linux-gnu/libc.so.6(+0x36cb0) [0x7fe46bddfcb0]
2 ./hello2() [0x4007b6]
3 ./hello2() [0x4007fa]
4 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fe46bdcaf45]
5 ./hello2() [0x4005e9]
段错误 (核心已转储)
使用addr2line命令检测:
addr2line -a 0x4006e9 -e hello2
root@WXNNB:/home/wxn/projects/test# addr2line -a 0x4006e9 -e hello2
0x00000000004006e9
/home/wxn/projects/test/hello2.c:13