gccでコンパイルしたC++プログラムがクラッシュした時、スタックトレースを自動的に出力する方法
概要
Linux環境では、gccを使ってC/C++のコードをコンパイルするのが一般的です。gccはglibcライブラリを利用しており、execinfo.hヘッダーに用意されているbacktrace()関数を使うことで、エラー発生時点の呼び出しスタック(スタックトレース)を取得できます。
この仕組みを応用すると、セグメンテーション違反(Segmentation Fault)などの致命的なエラーが発生した瞬間に、シグナルハンドラ経由で自動的にスタックトレースを出力させることが可能です。ここでは、その具体的な実装方法を紹介します。
サンプルコード
#include <iostream>
#include <execinfo.h>
#include <signal.h>
#include <cstdlib>
#include <unistd.h>
using namespace std;
void error_handler(int sig) {
void *array[10];
size_t size;
size = backtrace(array, 10); // 全エントリのポインタを取得
cout << "Error: signal " << sig << ":\n"; // エラーの種類を表示
backtrace_symbols_fd(array, size, STDERR_FILENO); // 標準エラー出力へ書き出し
exit(1);
}
void invalid_index() {
int *ptr = (int*) - 1;
cout << *ptr << endl; // セグメンテーション違反を引き起こす
}
void func1() {
invalid_index();
}
void func2() {
func1();
}
int main(int argc, char **argv) {
signal(SIGSEGV, error_handler); // SIGSEGV発生時にハンドラを実行
func2(); // 関数呼び出しの連鎖でエラーを発生させる
}実行結果
Error: signal 11: ./a.out(+0x825)[0x5579a31d7825] /lib/x86_64-linux-gnu/libc.so.6(+0x3ef20)[0x7f7689009f20] ./a.out(+0x880)[0x5579a31d7880] ./a.out(+0x8a1)[0x5579a31d78a1] ./a.out(+0x8ad)[0x5579a31d78ad] ./a.out(+0x8d5)[0x5579a31d78d5] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x7f7688fecb97] ./a.out(+0x71a)[0x5579a31d771a]
コードの解説
このプログラムでは、main()内でsignal(SIGSEGV, error_handler)により、セグメンテーション違反(シグナル番号11)が発生した際のハンドラを登録しています。
invalid_index()関数内で無効なアドレス(-1)にアクセスすることで、意図的にセグメンテーション違反を発生させています。エラーが起きるとerror_handler()が呼び出され、次のような流れで処理が行われます。
- backtrace(): 現在の呼び出しスタック上の戻りアドレスを最大10件まで配列に取得します。
- backtrace_symbols_fd(): 取得したアドレスを人間が読める形式の文字列に変換し、指定したファイルディスクリプタ(ここでは標準エラー出力)へ直接書き出します。
なお、ハンドラ内でbacktrace_symbols()ではなくbacktrace_symbols_fd()を使用しているのは、後者がmalloc()を呼び出さずシグナルセーフなためです。ヒープが破損している状況でも比較的安全に出力できます。
補足:より読みやすいトレースを得るには
上記の出力例ではアドレスのみが表示されています。コンパイル時に-gおよび-rdynamicオプションを付与すると、関数名付きのシンボル情報が出力されるようになります。
g++ -g -rdynamic main.cpp
さらに、出力されたアドレスに対してaddr2lineコマンドを使用すれば、クラッシュした正確なソースファイル名と行番号を特定することもできます。これらを組み合わせることで、デバッグ効率を大幅に向上させられます。
-
C++プログラムから外部アプリケーション(メモ帳など)を起動する方法
この記事では、C++プログラムを使ってメモ帳(Notepad)などのサードパーティ製アプリケーションを起動する方法を解説します。実装は非常にシンプルで、コマンドプロンプトで使うコマンドをそのままC++から呼び出すだけで実現できます。ポイントとなるのは、標準ライブラリの system() 関数です。この関数の引数にアプリケーション名(コマンド)を文字列として渡すと、OSがそのコマンドを実行し、対応するアプリケーションが起動します。サンプルコード#include <iostream> using namespace std; int main() { cout <<
-
C++で簡単なプログラムを作成する方法【初心者向けステップ解説】
C++で最初の簡単なプログラムを作成するには、まず開発環境を整え、その上でプログラムを書いて実行します。この記事では、非常にシンプルな「Hello World」プログラムを例に、C++プログラミングを始めるための手順をわかりやすく解説します。ステップ1:C++コンパイラを入手するC++の学習を始める前に、まず必要になるのがコンパイラです。主要なOSプラットフォーム向けに、優れた無料のC++コンパイラが多数公開されています。自分の環境に合ったものをダウンロードするか、tutorialspoint.comが提供するオンラインコンパイラ(https://www.tutorialspoint.com/