前言
本文参考的资料
https://blog.csdn.net/ithomer/article/details/5929720
core的原理和例子:https://blog.csdn.net/furzoom/article/details/50443092
https://blog.csdn.net/furzoom/article/details/50443116
gdb用法简介:https://blog.csdn.net/ithomer/article/details/5929720
gdb调试的应用场景
前提:在编译时加入-g
选项,把调试信息加到可执行文件中,例如g++ -g hello.cpp –o hello
或是在CMakeLists.txt文件的编译参数中加入-g,常见的应用如下
- gdb <program>
program也就是你的执行文件,一般在当前目录下。 - gdb <program> core
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。 - gdb <program> <PID>
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试它。program应该在PATH环境变量中搜索到。
参见:https://www.cnblogs.com/chaoyazhisi/p/5396096.html,本文暂不讨论此应用
1. gdb调试执行文件
gdb ./test(可执行文件)后进入gdb模式,常见命令如下:
设置断点:
b function(在function函数入口处设置断点)
b number(在第number行设置断点)
b (在当前行设置断点)
layout 多窗口查看相应信息:https://blog.csdn.net/zhangjs0322/article/details/10152279
layout src 查看源码
layout reg:查看寄存器信息
单步(finish跳出):
n (next)下一步,逐过程 n 3 下行三步
s (step)进函数,逐步
run 运行程序
p (print) 变量名/地址
bt 或者where ( 查看当前函数堆栈)
quit 退出gdb模式
查看调试:http://www.cnblogs.com/jiangzhaowei/p/8987069.html
2.gdb调试core文件
2.1 segmentation fault简介
当程序运行过程中出现Segmentation fault (core dumped)错误时,程序停止运行,并产生core文件。core文件是程序运行状态的内存映象。使用gdb调试core文件,可以帮助我们快速定位程序出现段错误的位置。当然,可执行程序编译时应加上-g编译选项,生成调试信息。
当程序访问的内存超出了系统给定的内存空间,就会产生Segmentation fault (core dumped),因此,段错误产生的情况主要有:
- 内存访问越界
由于使用错误的下标,导致数组访问越界
搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符
使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。 - 多线程程序使用了线程不安全的函数。
多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump - 非法指针
使用空指针
随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump. - 堆栈溢出.不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
首先通过ulimit命令查看一下系统是否配置支持了dump core的功能。通过ulimit -c或ulimit -a,可以查看core file大小的配置情况,如果为0,则表示系统关闭了dump core。可以通过ulimit -c unlimited
来打开。若发生了段错误,但没有core dump,是由于系统禁止core文件的生成。
2.2 gdb生成core文件的过程
- 修改CMakeLists.txt文件,将
CMAKE_BUILD_TYPE
设为Debug(如果不需要添加调试信息,就直接修改CMAKE_BUILD_TYPE的值),并设置debug版本的参数,如下所示:
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE -O0 -w -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE -O3 -w")
- 建立一个debug文件夹,进入文件夹,执行:
cmake –DCMAKE_BUILD_TYPE=Debug ..(..为CMakeLists.txt所在目录)
-
make -j10
//生成二进制文件 - 先执行:
ulimit -c unlimited
(打开dump core的功能),再sh run.sh
(执行相应的二进制文件) - gdb bin(promotion_tag) core.txt
2.3 生成core文件例子:空指针
main.cpp
#include <stdio.h>
int main(void)
{
printf("hello world! dump core for set value to NULL pointer/n");
*(char *)0 = 0;
return 0;
}
- 修改CMakeLists.txt文件,其中为了支持gdb调试而做了修改的是三个set语句.
[chengliying@localhost null_ptr]$ vim CMakeLists.txt
PROJECT(hello_word_debug)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_BUILD_TYPE "Debug")
add_executable(hello_word_debug main.cpp)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE -O0 -w -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE -O3 -w")
- 建立debug文件目录并编译
[chengliying@localhost null_ptr]$ mkdir debug
[chengliying@localhost null_ptr]$ ls
CMakeLists.txt debug main.cpp
[chengliying@localhost null_ptr]$ cd debug
[chengliying@localhost debug]$ cmake –DCMAKE_BUILD_TYPE=Debug ..
-- The C compiler identification is GNU 4.4.7
-- ...
-- Build files have been written to: /home/chengliying/gdb/null_ptr/debug
- 生成可执行二进制文件
[chengliying@localhost debug]$ make -j10
Scanning dependencies of target hello_word_debug
[ 50%] Building CXX object CMakeFiles/hello_word_debug.dir/main.cpp.o
[100%] Linking CXX executable hello_word_debug
[100%] Built target hello_word_debug
- 打开core开关,生成core文件
[chengliying@localhost debug]$ ulimit -c unlimited
[chengliying@localhost debug]$ ./hello_word_debug
Segmentation fault (core dumped)
[chengliying@localhost debug]$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake core.7835 hello_word_debug Makefile
- 查看core文件,可以发现bug在第5行,输入bt进一步查看堆栈信息
[chengliying@localhost debug]$ gdb ./hello_word_debug core.7835
Core was generated by `./hello_word_debug'.
Program terminated with signal 11, Segmentation fault.
#0 0x000000000040077c in main () at /home/chengliying/gdb/null_ptr/main.cpp:5
5 *(char *)0 = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.el6.x86_64 libgcc-4.4.7-3.el6.x86_64 libstdc++-4.4.7-3.el6.x86_64
(gdb) bt
#0 0x000000000040077c in main () at /home/chengliying/gdb/null_ptr/main.cpp:5
(gdb)
2.4栈溢出示例
有的错误不会直接显示源码,此时可以gdb模式下输入bt
查看堆栈信息,从下堆栈信息中,调“frame n“`显示相应的源码至找到bug所在。源码链接:https://blog.csdn.net/ithomer/article/details/5945152
类似的例子还有:https://www.cnblogs.com/luhouxiang/p/6830316.html
- 未显示源码的gdb调试信息
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
#0 0x00007ff24cc23283 in vfprintf () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.el6.x86_64
- 输入bt查看堆栈信息
(gdb) bt
#0 0x00007ff24cc23283 in vfprintf () from /lib64/libc.so.6
#1 0x00007ff24cc2e40a in printf () from /lib64/libc.so.6
#2 0x00000000004005e0 in test (s=0x0) at test.c:9
#3 0x000000000040060a in test (s=0x0) at test.c:13
- frame 查看相应的源码
(gdb) frame 2
#2 0x000000000040060a in test (s=0x0) at test.c:9
9 test(s);