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的方式需要每次都要執行一下這個命令,也可以修改系統配置,默認執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM