linux下获取崩溃调试信息

当程序因为内存等问题崩溃退出时,我们想要能获得当时退出的时候调用堆栈的信息,这样对于查找解决问题帮助非常大。

https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes

https://owent.net/2018/1801.html

https://www.jianshu.com/p/58d32fbd8dfa

https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c

第一种方式

注册SIGSEGV事件,调用backtrace

#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
using namespace std;
void handler(int sig)
{
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

void handler1(int sig)
{
  void *array[10];
  size_t size;
  char **func_name_cache;

  size = backtrace (array, 10);
  func_name_cache = backtrace_symbols (array, size);

  for (size_t i = 0; i < size; i++)
  {
    cout << func_name_cache[i] << endl;
  }

  free (func_name_cache);
}

void fun3()
{
  int *foo = (int*)-1;
  printf("%d\n", *foo);
}

void fun2()
{
  fun3();
}
void fun1()
{
  fun2();
}


int main(int argc, char **argv)
{
  signal(SIGSEGV, handler);
  fun1();
}

直接编译,无法得知对应的代码位置

$ g++ test3.cpp

$ ./a.out
Error: signal 11:
./a.out(+0x11a1)[0x5583aedc71a1]
/lib/x86_64-linux-gnu/libc.so.6(+0x37840)[0x7f3953c71840]
./a.out(+0x11fc)[0x5583aedc71fc]
./a.out(+0x121d)[0x5583aedc721d]
./a.out(+0x1229)[0x5583aedc7229]
./a.out(+0x1251)[0x5583aedc7251]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb)[0x7f3953c5e09b]
./a.out(+0x10ca)[0x5583aedc70ca]

$ addr2line 0x11fc -e a.out
??:?

使用addr2line也无法获得对应信息

增加-g参数附带调试信息,输出结果和上面一样,看不出具体的位置,但是使用addr2line可以获得具体位置

$ g++ test3.cpp -g

$ ./a.out
Error: signal 11:
./a.out(+0x11a1)[0x5628e34c41a1]
/lib/x86_64-linux-gnu/libc.so.6(+0x37840)[0x7f647aee9840]
./a.out(+0x11fc)[0x5628e34c41fc]
./a.out(+0x121d)[0x5628e34c421d]
./a.out(+0x1229)[0x5628e34c4229]
./a.out(+0x1251)[0x5628e34c4251]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb)[0x7f647aed609b]
./a.out(+0x10ca)[0x5628e34c40ca]

$ addr2line 0x11fc -e a.out
/home/xxx/code/test3.cpp:24

我们看到最后一个调用堆栈是24行,正好是我们打印异常指针崩溃的位置。

增加-rdynamic参数

$ g++ test3.cpp -g -rdynamic

$ ./a.out
Error: signal 11:
./a.out(_Z7handleri+0x1c)[0x555eedf2a1a1]
/lib/x86_64-linux-gnu/libc.so.6(+0x37840)[0x7f1915a12840]
./a.out(_Z4fun3v+0x14)[0x555eedf2a1fc]
./a.out(_Z4fun2v+0x9)[0x555eedf2a21d]
./a.out(_Z4fun1v+0x9)[0x555eedf2a229]
./a.out(main+0x25)[0x555eedf2a251]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb)[0x7f19159ff09b]
./a.out(_start+0x2a)[0x555eedf2a0ca]

输出结果变了,有函数的调用信息,但是函数具体调用在第几行,无法直观的看出,也无法使用addr2line了,因为它帮我们转了部分地址,无法得知相对于a.out的偏移位置了。

ulimit

这是一个shell命令,用于限定用户使用的系统资源,我们需要用到里面的-c参数,限定core文件的大小,后面跟一个参数,是数字,表示多少个block,也可以设置不限制大小。

$ ulimit -c 1024

然后使用-g编译程序。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>

void fun3()
{
  int *foo = (int*)-1;
  std::cout << *foo;
}

void fun2()
{
  fun3();
}
void fun1()
{
  fun2();
}


int main(int argc, char **argv)
{
  fun1();
}

运行程序,崩溃,在本地目录产生一个core文件,通过gdb调试,可以直接看到断点位置

$ gdb ./a.out core
GNU gdb (Debian 8.2.1-2+b3) 8.2.1
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-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 ./a.out...done.
[New LWP 11576]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000562563b58169 in fun3 () at test3.cpp:9
9         std::cout << *foo;
(gdb) bt
#0  0x0000562563b58169 in fun3 () at test3.cpp:9
#1  0x0000562563b58185 in fun2 () at test3.cpp:14
#2  0x0000562563b58191 in fun1 () at test3.cpp:18
#3  0x0000562563b581a8 in main (argc=1, argv=0x7ffef6c5b468) at test3.cpp:24
(gdb)

第三种方式

使用libunwind库,这个库使用下来,还是没有解决我们打印对应代码位置的问题,输出的内容与backtraces类似。

最后总结下来,就两种比较实用,一个就是backtrace,一个就是core文件。实用信号截获中断,打印堆栈信息的话,是无法产生core文件的。backtrace的方式一劳永逸,ulimit的方式需要每次都要执行一下这个命令,也可以修改系统配置,默认执行。

posted @ 2020-09-10 16:42  秋来叶黄  阅读(1090)  评论(0编辑  收藏  举报