5

シグナル ハンドラーで backtrace() の適切な解決策を見つけるために既に何度もグーグルを実行し、ほとんどすべてを試しましたが、シグナル ハンドラーでバックトレースを正常に取得できませんでした。これは SIGUSR1 ハンドラーではありません。

  • uclibc config で UCLIBC_HAS_BACKTRACE=y を有効にしてコンパイルする
  • libubacktrace.so が作成されていることを確認
  • 次のオプションでアプリケーションバイナリをコンパイルしました -g -rdynamic -fexception または -funwind-tables
  • バイナリ自体が「削除」されているようです

ただし、シグナル ハンドラから完全なバックトレースを取得できませんでした。シグナルハンドラで呼び出した関数アドレスのみが出力されました。

target-gdb バイナリを使用し、gdb --pid コマンドを使用してプロセスをアタッチすると、完全なバックトレースを適切に取得できました。

また、pstack を試してみましたが (pstack-1.2 - arm-patch を試しましたが、ひどい... 何も出力されませんでした) あまり役に立ちませんでした。

何かアドバイス?


1) Makefile のコンパイラ オプション

CFLAGS += -g -fexceptions -funwind-tables -Werror $(WARN) ...

2) コード

コードは非常にシンプルです。

#define CALLSTACK_SIZE 10

static void print_stack(void) {
    int i, nptrs;
    void *buf[CALLSTACK_SIZE + 1];
    char **strings;

    nptrs = backtrace(buf, CALLSTACK_SIZE);
    printf("%s: backtrace() returned %d addresses\n", __func__, nptrs);

    strings = backtrace_symbols(buf, nptrs);

    if(strings == NULL) {
        printf("%s: no backtrace captured\n", __func__);
        return;
    }

    for(i = 0; i < nptrs; i++) {
        printf("%s\n", strings[i]);
    }

    free(strings);
}

...
static void sigHandler(int signum)
{
    printf("%s: signal %d\n", __FUNCTION__, signum);
    switch(signum ) {
    case SIGUSR2:
        // told to quit
        print_stack();
        break;
    default:
        break;
    }
}
4

2 に答える 2

9

signal(7)signal-safety(7)をよく 読んでください。

シグナルハンドラは、(直接的または間接的に) async-signal-safe-functions (実際には、ほとんどのsyscalls(2)のみ) およびbacktrace(3)またはprintf(3)またはmalloc(3)freeのみを呼び出すように制限されています。非同期シグナルセーフ。したがって、コードは正しくありません: シグナル ハンドラーは間接的に (を介して)sigHandler呼び出しており、非同期シグナル セーフではありません。printfprint_stackfree

したがって、唯一のオプションはgdbデバッガーを使用することです。

POSIX signal.hシグナルの概念について詳しくは、こちらをご覧ください。実際には、シグナル ハンドラーが実行できるほとんど唯一の賢明なことは、何らかのグローバル、スレッド ローカル、または静的なvolatile sig_atomic_t フラグを設定することであり、これは別の場所でテストする必要があります。アプリケーションが別の場所 (たとえば、GUI アプリケーションの場合はイベント ループ内) で読み取るように、数バイトをpipe(7)に直接write(2)することもできます。

libbacktraceGCC 内からIan Taylor のものを使用することもできます (プログラムがデバッグ情報を使用してコンパイルされていると仮定します-g)。シグナルハンドラーでの動作は保証されていませんが (非同期シグナルセーフ関数のみを使用していないため)、実際には非常に便利です。

シグナルを処理するときに、カーネルがsigreturn(2)の呼び出しフレーム (呼び出しスタック内) を設定していることに注意してください。

(特にアプリケーションがシングルスレッドの場合) sigaltstack(2)を使用して、別のシグナル スタックを使用することもできます。役に立つかどうかわかりません。

イベント ループがある場合は、Linux 固有のsignalfd(2)の使用を検討し、イベント ループに要求するpollことができます。SIGTERMor SIGQUITorの場合SIGALRM、非常に便利なトリックです。

于 2015-05-01T06:40:42.990 に答える