web-dev-qa-db-ja.com

Cでスタックトレースを取得するにはどうすればよいですか?

これを行うための標準的なC関数がないことを知っています。私はWindowsと* nixでこれを行うテクニックは何だろうと思っていましたか? (Windows XPは今これを行う上で最も重要なOSです。)

78
Kevin

これをプロジェクトに使用しました:

https://www.codeproject.com/kb/threads/stackwalker.aspx

コードは少し厄介な私見ですが、うまく機能します。 Windowsのみ。

22
Mark

glibcはbacktrace()関数を提供します。

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

78
sanxiyn

Backtrace()とbacktrace_symbols()があります:

Manページから:

     #include <execinfo.h>
     #include <stdio.h>
     ...
     void* callstack[128];
     int i, frames = backtrace(callstack, 128);
     char** strs = backtrace_symbols(callstack, frames);
     for (i = 0; i < frames; ++i) {
         printf("%s\n", strs[i]);
     }
     free(strs);
     ...

これをより便利な/ OOPの方法で使用する1つの方法は、backtrace_symbols()の結果を例外クラスコンストラクターに保存することです。したがって、そのタイプの例外をスローすると、スタックトレースが発生します。次に、それを印刷する機能を提供します。例えば:


class MyException : public std::exception {

    char ** strs;
    MyException( const std::string & message ) {
         int i, frames = backtrace(callstack, 128);
         strs = backtrace_symbols(callstack, frames);
    }

    void printStackTrace() {
        for (i = 0; i 

...



try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

タダ!

注:最適化フラグを有効にすると、結果のスタックトレースが不正確になる場合があります。理想的には、デバッグフラグをオンにして最適化フラグをオフにしてこの機能を使用します。

28
Tom

Windowsの場合、StackWalk64()API(32ビットWindowsでも)を確認してください。 UNIXの場合は、OSのネイティブな方法を使用するか、使用可能な場合はglibcのbacktrace()にフォールバックする必要があります。

ただし、ネイティブコードでStacktraceを使用することはめったに良いアイデアではないことに注意してください。不可能ではないが、通常は間違ったことを達成しようとしているからです。

ほとんどの場合、例外がキャッチされたとき、アサートが失敗したとき、または最悪で最も間違っているときなど、例外的な状況でスタックトレースを取得しようとします。セグメンテーション違反。

最後の問題を考慮すると、ほとんどのAPIでは、メモリを明示的に割り当てる必要があるか、内部でメモリを割り当てる必要があります。あなたのプログラムが現在あるかもしれない脆弱な状態でそうすることは、実際に事態をさらに悪化させるかもしれません。たとえば、クラッシュレポート(またはコアダンプ)には、問題の実際の原因は反映されませんが、それを処理しようとして失敗します)。

ほとんどの人がスタックトレースを取得する際にそれを試しているように見えるので、あなたはその致命的なエラー処理を達成しようとしていると思います。もしそうなら、私はデバッガー(開発中)に依存し、本番環境(またはWindowsのミニダンプ)でプロセスをコアダンプさせます。適切なシンボル管理と一緒に、死後の指示を理解するのに問題はないはずです。

20
Christian.K

Windowsの場合、CaptureStackBackTrace()もオプションであり、StackWalk64()よりもユーザーの準備コードが少なくて済みます。 (また、私が持っていた同様のシナリオでは、CaptureStackBackTrace()StackWalk64()よりも(より確実に)うまく機能しました。)

4
Quasidart

nwind library を使用する必要があります。

unw_cursor_t cursor; unw_context_t uc;
unw_Word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
unsigned long a[100];
int ctr = 0;

while (unw_step(&cursor) > 0) {
  unw_get_reg(&cursor, UNW_REG_IP, &ip);
  unw_get_reg(&cursor, UNW_REG_SP, &sp);
  if (ctr >= 10) break;
  a[ctr++] = ip;
}

共有ライブラリから呼び出しを行わない限り、アプローチもうまく機能します。

Linuxでaddr2lineコマンドを使用して、対応するPCのソース関数/行番号を取得できます。

4
user262802

プラットフォームに依存しない方法はありません。

最も近い方法は、最適化なしでコードを実行することです。そのようにして、プロセスにアタッチして(ビジュアルc ++デバッガーまたはGDBを使用)、使用可能なスタックトレースを取得できます。

4

私の記事を紹介させてください。それはほんの数行のコードです。

死後のデバッグ

これのx64実装 には現在問題がありますが。

2
RED SOFT ADAIR

Solarisには pstack コマンドがあり、これもLinuxにコピーされました。

2
wnoise

過去数年間、Ian Lance Taylorのlibbacktraceを使用しています。すべてのシンボルをエクスポートする必要があるGNU Cライブラリの関数よりもはるかにクリーンです。バックトレースの生成にlibunwindよりも多くのユーティリティを提供します。 addr2lineなどの外部ツールを必要とするアプローチと同様に、ASLRによって。

Libbacktraceは当初GCCディストリビューションの一部でしたが、著者によってBSDライセンスの下でスタンドアロンライブラリとして利用できるようになりました。

https://github.com/ianlancetaylor/libbacktrace

この記事の執筆時点では、libbacktraceでサポートされていないプラットフォームでバックトレースを生成する必要がない限り、他のものは使用しません。

0
Erwan Legrand