これを行うための標準的なC関数がないことを知っています。私はWindowsと* nixでこれを行うテクニックは何だろうと思っていましたか? (Windows XPは今これを行う上で最も重要なOSです。)
これをプロジェクトに使用しました:
https://www.codeproject.com/kb/threads/stackwalker.aspx
コードは少し厄介な私見ですが、うまく機能します。 Windowsのみ。
glibcはbacktrace()関数を提供します。
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
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();
}
タダ!
注:最適化フラグを有効にすると、結果のスタックトレースが不正確になる場合があります。理想的には、デバッグフラグをオンにして最適化フラグをオフにしてこの機能を使用します。
Windowsの場合、StackWalk64()API(32ビットWindowsでも)を確認してください。 UNIXの場合は、OSのネイティブな方法を使用するか、使用可能な場合はglibcのbacktrace()にフォールバックする必要があります。
ただし、ネイティブコードでStacktraceを使用することはめったに良いアイデアではないことに注意してください。不可能ではないが、通常は間違ったことを達成しようとしているからです。
ほとんどの場合、例外がキャッチされたとき、アサートが失敗したとき、または最悪で最も間違っているときなど、例外的な状況でスタックトレースを取得しようとします。セグメンテーション違反。
最後の問題を考慮すると、ほとんどのAPIでは、メモリを明示的に割り当てる必要があるか、内部でメモリを割り当てる必要があります。あなたのプログラムが現在あるかもしれない脆弱な状態でそうすることは、実際に事態をさらに悪化させるかもしれません。たとえば、クラッシュレポート(またはコアダンプ)には、問題の実際の原因は反映されませんが、それを処理しようとして失敗します)。
ほとんどの人がスタックトレースを取得する際にそれを試しているように見えるので、あなたはその致命的なエラー処理を達成しようとしていると思います。もしそうなら、私はデバッガー(開発中)に依存し、本番環境(またはWindowsのミニダンプ)でプロセスをコアダンプさせます。適切なシンボル管理と一緒に、死後の指示を理解するのに問題はないはずです。
Windowsの場合、CaptureStackBackTrace()
もオプションであり、StackWalk64()
よりもユーザーの準備コードが少なくて済みます。 (また、私が持っていた同様のシナリオでは、CaptureStackBackTrace()
はStackWalk64()
よりも(より確実に)うまく機能しました。)
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のソース関数/行番号を取得できます。
プラットフォームに依存しない方法はありません。
最も近い方法は、最適化なしでコードを実行することです。そのようにして、プロセスにアタッチして(ビジュアルc ++デバッガーまたはGDBを使用)、使用可能なスタックトレースを取得できます。
Solarisには pstack コマンドがあり、これもLinuxにコピーされました。
過去数年間、Ian Lance Taylorのlibbacktraceを使用しています。すべてのシンボルをエクスポートする必要があるGNU Cライブラリの関数よりもはるかにクリーンです。バックトレースの生成にlibunwindよりも多くのユーティリティを提供します。 addr2line
などの外部ツールを必要とするアプローチと同様に、ASLRによって。
Libbacktraceは当初GCCディストリビューションの一部でしたが、著者によってBSDライセンスの下でスタンドアロンライブラリとして利用できるようになりました。
https://github.com/ianlancetaylor/libbacktrace
この記事の執筆時点では、libbacktraceでサポートされていないプラットフォームでバックトレースを生成する必要がない限り、他のものは使用しません。