86

これを行うための標準 C 関数がないことはわかっています。Windowsと* nixでこれを行うためのテクニックは何ですか? (Windows XP は、現時点でこれを行うのに最も重要な OS です。)

4

9 に答える 9

82

glibc はbacktrace()機能を提供します。

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

于 2008-09-19T21:15:32.027 に答える
32

backtrace()、およびbacktrace_symbols():

マニュアルページから:

#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 < frames; ++i) {
            printf("%s\n", strs[i]);
        }
        free(strs);
    }
};

...

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

タダ!

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

于 2010-02-25T19:07:58.157 に答える
22

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

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

ほとんどの場合、例外がキャッチされたとき、アサートが失敗したとき、または致命的な「例外」または次のようなシグナルを受け取ったときなど、例外的な状況でスタックトレースを取得しようとします。セグメンテーション違反。

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

スタックトレースの取得に関しては、ほとんどの人がそれを試みているように見えるので、致命的なエラー処理を達成しようとしていると思います。もしそうなら、私はデバッガー (開発中) に依存し、本番環境でプロセスのコアダンプ (または Windows のミニダンプ) を許可します。適切なシンボル管理と合わせて、原因となっている命令の事後分析を問題なく行う必要があります。

于 2008-09-24T13:07:58.227 に答える
5

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

于 2008-10-01T21:35:37.657 に答える
5

アンワインド ライブラリを使用する必要があります。

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 のソース関数/行番号を取得できます。

于 2010-01-31T06:31:55.703 に答える
4

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

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

于 2008-09-19T21:15:16.473 に答える
2

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

于 2008-09-22T02:15:34.567 に答える
0

スタックを後方に歩くことでそれを行うことができます。ただし、実際には、各関数の先頭でコール スタックに識別子を追加し、最後にそれをポップして、その内容を出力するだけの方が簡単な場合がよくあります。これは少し PITA ですが、うまく機能し、最終的に時間を節約できます。

于 2008-09-19T21:15:13.773 に答える