Unix システム上のスタック トレースを自動的に取得する
-
09-06-2019 - |
質問
Unix システムでスタック トレースを自動的に取得するにはどのような方法がありますか?単にコア ファイルを取得したり、GDB と対話的にアタッチしたりするだけではなく、テキスト ファイルにバックトレースをダンプする SIGSEGV ハンドラーを用意することを意味します。
以下のオプション機能のボーナスポイント:
- クラッシュ時の追加情報収集 (例:設定ファイル)。
- クラッシュ情報バンドルを開発者に電子メールで送信します。
- これを追加する機能
dlopen
編集済み共有ライブラリ - GUIを必要としない
解決
BSD を搭載したシステムを使用している場合 backtrace
利用可能な機能 (もちろん Linux、OSX 1.5、BSD) を使用すると、これをシグナル ハンドラーでプログラム的に行うことができます。
例えば (backtrace
IBM の例から派生したコード):
#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void sig_handler(int sig)
{
void * array[25];
int nSize = backtrace(array, 25);
char ** symbols = backtrace_symbols(array, nSize);
for (int i = 0; i < nSize; i++)
{
puts(symbols[i]);;
}
free(symbols);
signal(sig, &sig_handler);
}
void h()
{
kill(0, SIGSEGV);
}
void g()
{
h();
}
void f()
{
g();
}
int main(int argc, char ** argv)
{
signal(SIGSEGV, &sig_handler);
f();
}
出力:
0 a.out 0x00001f2d sig_handler + 35
1 libSystem.B.dylib 0x95f8f09b _sigtramp + 43
2 ??? 0xffffffff 0x0 + 4294967295
3 a.out 0x00001fb1 h + 26
4 a.out 0x00001fbe g + 11
5 a.out 0x00001fcb f + 11
6 a.out 0x00001ff5 main + 40
7 a.out 0x00001ede start + 54
これにはオプション機能のボーナス ポイントはありません (GUI が必要ないことを除く) が、非常にシンプルであり、追加のライブラリやプログラムを必要としないという利点があります。
他のヒント
ご参考までに、
提案された解決策 (シグナル ハンドラーで backtrace_symbols を使用する) は危険なほど壊れています。使用しないでください -
はい、backtrace と backtrace_symbols はバックトレースを生成し、それをシンボル名に変換します。
backtrace_symbols は malloc を使用してメモリを割り当てます。解放するには free を使用します。メモリ破損が原因でクラッシュしている場合は、malloc アリーナが破損し、二重フォールトを引き起こす可能性が非常に高くなります。
malloc と free は、内部でロックを使用して malloc アリーナを保護します。ロックが取得された malloc/free の途中でフォールトが発生した可能性があります。その場合、これらの関数またはそれらを呼び出す関数がデッドロックになります。
これもロックで保護されている標準ストリームを使用する put を使用します。printf の途中でエラーが発生すると、再びデッドロックが発生します。
32 ビット プラットフォーム上 (例:2年前の通常のPC)、カーネルは、スタック内の障害のある関数の代わりに、内部のglibc関数にリターンアドレスを植え付けます。そのため、あなたが興味がある唯一の最も重要な情報は、どの関数でプログラムに障害が発生したかです。 、これらのプラットフォームでは実際に破損します。
したがって、この例のコードは最悪の種類の間違いです。正常に動作しているように見えますが、運用環境では予期せぬ形で実際に失敗することになります。
ところで、それに興味がありますか?チェック これ 外。
乾杯、ギラド。
デマングラーを使用して詳細情報を取得する方法の例を次に示します。ご覧のとおり、これはスタックトレースをファイルに記録します。
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <cxxabi.h>
void sig_handler(int sig)
{
std::stringstream stream;
void * array[25];
int nSize = backtrace(array, 25);
char ** symbols = backtrace_symbols(array, nSize);
for (unsigned int i = 0; i < size; i++) {
int status;
char *realname;
std::string current = symbols[i];
size_t start = current.find("(");
size_t end = current.find("+");
realname = NULL;
if (start != std::string::npos && end != std::string::npos) {
std::string symbol = current.substr(start+1, end-start-1);
realname = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
}
if (realname != NULL)
stream << realname << std::endl;
else
stream << symbols[i] << std::endl;
free(realname);
}
free(symbols);
std::cerr << stream.str();
std::ofstream file("/tmp/error.log");
if (file.is_open()) {
if (file.good())
file << stream.str();
file.close();
}
signal(sig, &sig_handler);
}
Dereks の解決策がおそらく最善ですが、とにかく代替案を次に示します。
最近の Linux カーネル バージョンでは、コア ダンプをスクリプトまたはプログラムにパイプすることができます。コア ダンプを取得し、必要な追加情報を収集し、すべてをメールで返送するスクリプトを作成できます。ただし、これはグローバル設定であるため、システム上でクラッシュするプログラムに適用されます。セットアップするには root 権限も必要です。これは、/proc/sys/kernel/core_pattern ファイルを通じて構成できます。それを '|のようなものに設定します/home/myuser/bin/my-core handler-script '。
Ubuntu の人もこの機能を使用しています。