1. ホーム
  2. c++

[解決済み] プログラムがクラッシュしたときにスタックトレースを自動的に生成する方法

2022-03-15 17:47:18

質問

LinuxでGCCコンパイラを使って作業しています。わたしのC++プログラムがクラッシュしたとき、自動的にスタックトレースを生成してほしいのですが、可能でしょうか?

私のプログラムは多くの異なるユーザーによって実行されており、Linux、Windows、Macintoshで動作しています(すべてのバージョンは、以下の方法でコンパイルされています)。 gcc ).

私のプログラムがクラッシュしたときにスタックトレースを生成できるようにしたいのですが、ユーザーが次に実行するときに、私が問題を追跡できるように、スタックトレースを私に送信してもよいかどうかを尋ねます。情報を私に送ることはできますが、トレース文字列を生成する方法がわかりません。何かアイデアはありますか?

解決方法を教えてください。

Linux と Mac OS X では、gcc または glibc を使用するコンパイラを使用している場合、バックトレース() 関数を使用することができます。 execinfo.h を使用すると、スタックトレースを表示し、セグメンテーションフォールトが発生したときに優雅に終了することができます。 ドキュメントはこちらです。 libcのマニュアルにある .

をインストールするサンプルプログラムです。 SIGSEGV ハンドラーにスタックトレースを出力します。 stderr がセグメンテーションに失敗したとき。 このとき baz() 関数は、ハンドラのトリガーとなるセグメンテーションフォールトを引き起こします。

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


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 baz() {
 int *foo = (int*)-1; // make a bad pointer
  printf("%d\n", *foo);       // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }


int main(int argc, char **argv) {
  signal(SIGSEGV, handler);   // install our handler
  foo(); // this will call foo, bar, and baz.  baz segfaults.
}

でコンパイルします。 -g -rdynamic は出力にシンボル情報を取得し、glibc はそれを使ってきれいなスタックトレースを作ることができます。

$ gcc -g -rdynamic ./test.c -o test

これを実行すると、次のような出力が得られます。

$ ./test
Error: signal 11:
./test(handler+0x19)[0x400911]
/lib64/tls/libc.so.6[0x3a9b92e380]
./test(baz+0x14)[0x400962]
./test(bar+0xe)[0x400983]
./test(foo+0xe)[0x400993]
./test(main+0x28)[0x4009bd]
/lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3a9b91c4bb]
./test[0x40086a]

これは、スタック内の各フレームのロードモジュール、オフセット、および関数を示しています。 ここでは、スタックの一番上にあるシグナルハンドラと、その前にある libc 関数を見ることができます。 main に加えて main , foo , bar および baz .