これを行うための標準のC関数がないことを私は知っています。私はWindowsと* nixでこれを行うためのテクニックは何だろうと思っていましたか?(Windows XPは、今これを行うための私の最も重要なOSです。)
回答:
glibcはbacktrace()関数を提供します。
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
char ** backtrace_symbols (void *const *buffer, int size)
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
たとえば、出力をstdout / errに直接送信できるものもあります。
backtrace_symbols()
吸う。すべてのシンボルをエクスポートする必要があり、DWARF(デバッグ)シンボルをサポートしていません。libbacktraceは、多くの(ほとんどの)場合にはるかに優れたオプションです。
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();
}
タダ!
注:最適化フラグを有効にすると、結果のスタックトレースが不正確になる可能性があります。理想的には、デバッグフラグをオンにし、最適化フラグをオフにして、この機能を使用します。
Windowsの場合は、StackWalk64()APIを確認してください(32ビットWindowsでも)。UNIXの場合は、OSのネイティブな方法を使用するか、使用可能な場合はglibcのbacktrace()にフォールバックする必要があります。
ただし、ネイティブコードでスタックトレースを使用することはめったに良い考えではないことに注意してください。それが不可能なためではなく、通常は間違ったことを達成しようとしているためです。
ほとんどの場合、人々は、例外がキャッチされたとき、アサーションが失敗したとき、または-最悪で最も間違っている-致命的な「例外」または次のような信号を受け取ったときなど、例外的な状況でスタックトレースを取得しようとしますセグメンテーション違反。
最後の問題を考慮すると、ほとんどのAPIでは、メモリを明示的に割り当てる必要があるか、内部で割り当てる場合があります。プログラムが現在置かれている可能性のある脆弱な状態でこれを行うと、事態がさらに悪化する可能性があります。たとえば、クラッシュレポート(またはコアダンプ)は問題の実際の原因を反映していませんが、それを処理しようとして失敗しました)。
スタックトレースを取得することになると、ほとんどの人がそれを試みているように見えるので、あなたはその致命的なエラー処理を達成しようとしていると思います。もしそうなら、私はデバッガー(開発中)に依存し、プロセスを本番環境でコアダンプ(またはWindowsでミニダンプ)させます。適切なシンボル管理と一緒に、あなたは死後に原因となる指示を理解するのに問題がないはずです。
アンワインドライブラリを使用する必要があります。
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;
}
共有ライブラリから電話をかけない限り、あなたのアプローチもうまくいきます。
addr2line
Linuxでコマンドを使用して、対応するPCのソース関数/行番号を取得できます。
プラットフォームに依存しない方法はありません。
最も近い方法は、最適化せずにコードを実行することです。このようにして、(ビジュアルc ++デバッガーまたはGDBを使用して)プロセスにアタッチし、使用可能なスタックトレースを取得できます。
Solarisにはpstackコマンドがあり、これもLinuxにコピーされています。
スタックを後方に歩くことでそれを行うことができます。ただし、実際には、各関数の最初にある呼び出しスタックに識別子を追加し、最後にそれをポップしてから、内容を印刷する方が簡単な場合がよくあります。これは少しPITAですが、うまく機能し、最終的には時間を節約できます。
過去数年間、私はIan LanceTaylorのlibbacktraceを使用してきました。これは、すべてのシンボルをエクスポートする必要があるGNUCライブラリの関数よりもはるかにクリーンです。libunwindよりもバックトレースの生成に多くのユーティリティを提供します。そして最後に重要なことですが、などの外部ツールを必要とするアプローチのように、ASLRによって打ち負かされることはありませんaddr2line
。
Libbacktraceは当初GCCディストリビューションの一部でしたが、現在はBSDライセンスの下でスタンドアロンライブラリとして作成者によって利用可能になっています。
https://github.com/ianlancetaylor/libbacktrace
これを書いている時点では、libbacktraceでサポートされていないプラットフォームでバックトレースを生成する必要がない限り、他には何も使用しません。