7

Unixシステムでスタックトレースを自動的に取得するための方法は何ですか?コアファイルを取得したり、GDBと対話的に接続したりするだけでなく、バ​​ックトレースをテキストファイルにダンプするSIGSEGVハンドラーを使用することを意味します。

次のオプション機能のボーナスポイント:

  • クラッシュ時に収集される追加情報(構成ファイルなど)。
  • クラッシュ情報バンドルを開発者に電子メールで送信します。
  • dlopenこれをed共有ライブラリに追加する機能
  • GUIを必要としない
4

4 に答える 4

15

ご参考までに、

提案された解決策 (シグナル ハンドラーで backtrace_symbols を使用) は危険なほど壊れています。使用しないでください -

はい、backtrace と backtrace_symbols は backtrace を生成し、それをシンボリック名に変換します。

  1. backtrace_symbols は malloc を使用してメモリを割り当て、free を使用して解放します - メモリの破損が原因でクラッシュしている場合、malloc アリーナが破損している可能性が高く、二重障害が発生します。

  2. malloc と free は、malloc アリーナを内部的にロックで保護します。ロックが取得された状態で malloc/free の途中で障害が発生した可能性があります。これにより、これらの関数またはそれらを呼び出すものはすべてデッドロックになります。

  3. これもロックで保護されている標準ストリームを使用する puts を使用します。printf の途中で失敗すると、再びデッドロックが発生します。

  4. 32 ビット プラットフォーム (たとえば、2 年前の通常の PC) では、カーネルは、スタック内のフォールト関数の代わりに内部 glibc 関数へのリターン アドレスを植えます。関数がプログラム エラーを起こした場合、これらのプラットフォームでは実際に破損します。

したがって、例のコードは最悪の種類の間違いです。動作しているように見えますが、本番環境では予期しない方法で実際に失敗します。

ところで、それを正しく行うことに興味がありますか? これをチェックしてください。

乾杯、ギラッド。

于 2008-11-20T13:36:48.563 に答える
7

BSDbacktrace機能が利用可能なシステム (もちろん Linux、OSX 1.5、BSD) を使用している場合は、シグナル ハンドラでプログラムによってこれを行うことができます。

例 ( backtraceIBM の例から派生したコード):

#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 を必要としないことを除いて) のボーナス ポイントを獲得しませんが、非常に単純であり、追加のライブラリやプログラムを必要としないという利点があります。

于 2008-08-20T17:36:55.697 に答える
3

デマングラーを使用して詳細情報を取得する方法の例を次に示します。ご覧のとおり、これもスタックトレースをファイルに記録します。

#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);
}
于 2008-09-03T15:22:20.570 に答える
2

Dereks のソリューションがおそらく最適ですが、とにかく別の方法があります。

最新の Linux カーネル バージョンでは、コア ダンプをスクリプトまたはプログラムにパイプできます。コア ダンプをキャッチするスクリプトを作成し、必要な追加情報を収集して、すべてを返送することができます。ただし、これはグローバル設定であるため、システム上のクラッシュするプログラムに適用されます。また、セットアップには root 権限が必要です。/proc/sys/kernel/core_pattern ファイルで構成できます。それを ' | のようなものに設定します。/home/myuser/bin/my-core-handler-script'.

Ubuntuの人々もこの機能を使用しています。

于 2008-08-23T06:31:11.717 に答える