gdb 调试 与 coredump_我的笔记_

文章目录


点击我, 到底部

gdb 调试 与 coredump

gdb 是 GNU 发布的一个强大的程序调解工具, 也是Linux 程序员不可或缺的一大利器. 本章我们将给出 gdb 常用的命令的操作说明.

启动 gdb

使用gdb的前提

gcc -g hello.c -o hello

启动 gdb 调试:

gdb hello

然后就可以进入命令行操作,和Shell一样,gdb 支持命令补全。输入几个字母,按Tab键,
gdb会补全命令。按两次Tab,会提示所有可能的命令。
另外,其还支持命令缩写,如,h 代表help,
常用命令如下:

gdb常用命令

下面是实操

➜  makefile_demo git:(main) ✗ gdb hello_demo
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-11.el8
Copyright (C) 2018 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-redhat-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_demo...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x40059a
(gdb) r
Starting program: /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/makefile_demo/hello_demo
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64

Breakpoint 1, 0x000000000040059a in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.
Hello weifc.
count: 1
Hello world.
count: 2
0x00007ffff7a347b3 in __libc_start_main () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7a347b3 in __libc_start_main () from /lib64/libc.so.6
#1  0x00000000004004de in _start ()
(gdb)

bt 查看堆栈情况

bt 每次程序调用一个函数, 函数的地址, 参数, 函数内部变量都会被压入 “栈(Stack)” 中, 运行时堆栈信息对程序员非常重要, 使用 “bt” 命令可以看到运行时 栈的情况.
gdb 调试 与 coredump_我的笔记_
利用gdb单步执行理解刚接手的程序的时候,提示下图中的错误:

Breakpoint 1, 0x000000000040059a in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.
Hello weifc.
count: 1
Hello world.
count: 2
0x00007ffff7a347b3 in __libc_start_main () from /lib64/libc.so.6
(gdb)

需要升级了, 不鸟它, 直接cmake

退出 gdb

调试完毕, 使用 quit 命令(缩写为q) 退出 gdb 程序

gdb - coredump 详解

Coredump 调试

coredump 是什么?

程序异常退出是, 会产生一个 core 文件, 该文件记录了程序运行时的内存, 寄存器状态, 堆栈指针, 内存管理信息还有各种函数调用堆栈信息等, 我们可以理解为是程序工作当前状态存储生成的一文件, 通过工具分析这个文件, 我们可以定位到程序异常退出的时候对应的堆栈调用等信息, 找出问题所在并进行及时解决.
说人话, 也就是问题的现场

前期设置

  1. 设置 core 文件生成的目录, 其中 %e 表示文件名, %p 表示进程ID,
    否则会在程序的当前目录生成 dore 文件;
echo /data/coredump/core.%e.%p>/proc/sys/kernel/core_pattern

%e具体的执行程序, %p 进程ID

/proc/sys/kernel/core_pattern ## 这是固定的
➜  / mkdir data
➜  / ls
apps  bin  boot  data  dev  etc  home  lib  lib64  media  mnt  opt  patch  proc  root  run  sbin  srv  sys  tmp  usr  var  www
➜  /
➜  / cd data
➜  /data ls
➜  /data
➜  /data mkdir coredump
....
➜  build git:(main) ✗ echo /data/coredump/core.%e.%p>/proc/sys/kernel/core_pattern
➜  build git:(main) ✗ cat /proc/sys/kernel/core_pattern
/data/coredump/core.%e.%p
➜  build git:(main) ✗

新建/data/coredump, 然后 echo
这是第一步

  1. 当前执行程序的用户对 core 目录有写权限且有足够的空间存储 core 文件;
➜  /data ls -la
total 4
drwxr-xr-x   3 root root   22 Jan 11 13:21 .
dr-xr-xr-x. 22 root root 4096 Jan 11 13:21 ..
drwxr-xr-x   2 root root    6 Jan 11 13:21 coredump
➜  /data

第二步

  1. 生成不限制的 core 文件;
➜  /data
➜  /data ls -la
total 4
drwxr-xr-x   3 root root   22 Jan 11 13:21 .
dr-xr-xr-x. 22 root root 4096 Jan 11 13:21 ..
drwxr-xr-x   2 root root    6 Jan 11 13:21 coredump
➜  /data
➜  /data
➜  /data
➜  /data ulimit -a
-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         unlimited
-m: resident set size (kbytes)      unlimited
-u: processes                       7186
-n: file descriptors                65535
-l: locked-in-memory size (kbytes)  64
-v: address space (kbytes)          unlimited
-x: file locks                      unlimited
-i: pending signals                 7186
-q: bytes in POSIX msg queues       819200
-e: max nice                        0
-r: max rt priority                 0
-N 15:                              unlimited
➜  /data

gdb 调试 与 coredump_我的笔记_
如果箭头的 内容 是 0,
就输入下面的命令

ulimit -c unlimited

什么情况下会导致程序异常退出

非法指针的访问, 堆栈溢出

如何调试

  1. 编译的时候添加-g 选项, 增加调试信息
  2. gdb program core_file

bt 或者 where 查看调用栈信息

如果你要查看某一层的信息, 你需要切换当前的栈, 一般来说, 程序停止时, 最顶层的栈就是当前栈, 如果你要查看栈下面层的详细信息, 首先要做的是切换当前栈.

frame <n>
f<n>

n 是一个从 0 开始的整数, 是栈中的层编号. 比如: frame 0, 表示栈顶, frame 1, 表示栈的第二层.

up<n> 
	表示向栈的上面移动 n 层, 可以不打n, 表示向上移动一层.

down<n>
	表示向栈的下面移动 n 层, 可以不打n, 表示向下移动一层

具体栗子

coredump_test.cc

#include <stdio.h>

int func(int* p) {
    int y = *p;
    return y;
}

int main() {
    int* p = NULL;
    return func(p);
}

用肉眼都可以看出, 问题但是如果是成百上千?
运行结果

➜  build git:(main) ✗ ./bin/coredump_test
[1]    2325240 segmentation fault (core dumped)  ./bin/coredump_test
➜  build git:(main) ✗

说明报错了!
gdb 调试 与 coredump_我的笔记_

程序异常退出时, 会产生一个 core 文件.
有错误的现场了, 但是怎么看, 错误的现场呢?

下面我们用 coredump 来查找出错误在那个位置

➜  build git:(main) ✗ gdb ./bin/coredump_test /data/coredump/core.coredump_test.2335299
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-11.el8
Copyright (C) 2018 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-redhat-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 ./bin/coredump_test...done.

warning: exec file is newer than core file.
[New LWP 2335299]

warning: Loadable section ".note.gnu.property" outside of ELF segments

warning: Loadable section ".note.gnu.property" outside of ELF segments
Core was generated by `./bin/coredump_test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
5           return y;
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64 libgcc-8.3.1-5.1.el8.x86_64 libstdc++-8.3.1-5.el8.0.2.x86_64
(gdb)

上面就是用coredump查找问题的步骤, 具体到哪一行了. 这样就没毛病

更详细的调试

➜  build git:(main) ✗ gdb ./bin/coredump_test /data/coredump/core.coredump_test.2335299
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-11.el8
Copyright (C) 2018 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-redhat-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 ./bin/coredump_test...done.

warning: exec file is newer than core file.
[New LWP 2335299]

warning: Loadable section ".note.gnu.property" outside of ELF segments

warning: Loadable section ".note.gnu.property" outside of ELF segments
Core was generated by `./bin/coredump_test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5

warning: Source file is more recent than executable.
5           return y;
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64 libgcc-8.3.1-5.1.el8.x86_64 libstdc++-8.3.1-5.el8.0.2.x86_64
(gdb) bt
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
#1  0x00007f7b70ed57b3 in __libc_start_main () from /lib64/libc.so.6
#2  0x000000000040065e in _start () at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:10
(gdb) where
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
#1  0x00007f7b70ed57b3 in __libc_start_main () from /lib64/libc.so.6
#2  0x000000000040065e in _start () at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:10
(gdb) bt
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
#1  0x00007f7b70ed57b3 in __libc_start_main () from /lib64/libc.so.6
#2  0x000000000040065e in _start () at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:10
(gdb) where
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
#1  0x00007f7b70ed57b3 in __libc_start_main () from /lib64/libc.so.6
#2  0x000000000040065e in _start () at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:10
(gdb) frame 0
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
5           return y;
(gdb) frame 1
#1  0x00007f7b70ed57b3 in __libc_start_main () from /lib64/libc.so.6
(gdb) frame 0
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
5           return y;
(gdb) f
#0  func (p=0x0) at /home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5
5           return y;

打印更详细信息

(gdb) info f
Stack level 0, frame at 0x7ffd1d15a8b0:
 rip = 0x400720 in func (/home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/coredump_test.cc:5); saved rip = 0x7f7b70ed57b3
 called by frame at 0x7ffd1d15a970
 source language c++.
 Arglist at 0x7ffd1d15a8a0, args: p=0x0
 Locals at 0x7ffd1d15a8a0, Previous frame's sp is 0x7ffd1d15a8b0
 Saved registers:
  rip at 0x7ffd1d15a8a8
(gdb) info args
p = 0x0
(gdb) info loals
Undefined info command: "loals".  Try "help info".
(gdb) info locals
y = <error reading variable y (Cannot access memory at address 0x0)>
(gdb)

gdb - 直接打印堆栈

例子

demo_log1.cc

#include <stdio.h>
#include <signal.h>
#include <execinfo.h>
#include <stdlib.h>

//信号钩子函数, 获取栈信息, 然后打印
void handle_segv(int signum) {
    void* array[100];
    size_t size;
    char** strings;
    size_t i;

    signal(signum, SIG_DFL);

    size = backtrace(array, 100);
    strings = (char**)backtrace_symbols(array, size);

    printf("Launchar received SIG: %d Stack trace: \n", signum);

    for (i = 0; i < size; i++) {
        fprintf(stderr, "%d %s\n", i, strings[i]);
    }

    free(strings);
}

int func(int* p) {
    int y = *p;

    return y;
}

int main() {
    int* p = NULL;
    signal(SIGSEGV, handle_segv);
    signal(SIGABRT, handle_segv);

    return func(p);
}

运行结果

➜  build git:(main) ✗ ./bin/demo_log1
Launchar received SIG: 11 Stack trace:
0 ./bin/demo_log1(_Z11handle_segvi+0x28) [0x4009a8]
1 /lib64/libc.so.6(+0x37880) [0x7f49d8de9880]
2 ./bin/demo_log1(_Z4funcPi+0) [0x400a20]
3 /lib64/libc.so.6(__libc_start_main+0xf3) [0x7f49d8dd57b3]
4 ./bin/demo_log1(_start+0x2e) [0x4008be]
[1]    2349507 segmentation fault (core dumped)  ./bin/demo_log1
➜  build git:(main) ✗

使用addr2line 命令检测

0x4009a8, 这是什么意思. 下面的命令

addr2line -a 0x4009a8 -e demo_log1
➜  build git:(main) ✗ ./bin/demo_log1
Launchar received SIG: 11 Stack trace:
0 ./bin/demo_log1(_Z11handle_segvi+0x28) [0x4009a8]
1 /lib64/libc.so.6(+0x37880) [0x7fa27d3d2880]
2 ./bin/demo_log1(_Z4funcPi+0) [0x400a20]
3 /lib64/libc.so.6(__libc_start_main+0xf3) [0x7fa27d3be7b3]
4 ./bin/demo_log1(_start+0x2e) [0x4008be]
[1]    2350004 segmentation fault (core dumped)  ./bin/demo_log1
➜  build git:(main) ✗
➜  build git:(main) ✗ addr2line -a 0x4009a8 -e demo_log1
addr2line: 'demo_log1': No such file
➜  build git:(main) ✗ addr2line -a 0x4009a8 -e ./bin/demo_log1
0x00000000004009a8
/home/working/Volume_6_Matin_Linux-C_Learning/study_test01/text_coredump/tests/demo_log1.cc:16
➜  build git:(main) ✗
➜  build git:(main) ✗

查找得出在第16行, 注意addr2line -a 0x4009a8 -e demo_log1(可自执行的文件)

注意: 编译时一定要带上 -g

点击我, 操作具体栗子

点击我, 到顶部
上一篇:如何解coredump


下一篇:Runtime 函数 Swizzling 改变OC方法的调度顺序