c++ - 間違っ - 現在の呼び出し履歴のフレームのソースを表示するには




CまたはC++での呼び出しスタックの印刷 (7)

特定の関数が呼び出されるたびにCまたはC ++の実行中のプロセスにコールスタックをダンプする方法はありますか? 私が心に留めていることは、このようなものです:

void foo()
{
   print_stack_trace();

   // foo's body

   return
}

print_stack_traceは、Perlのcallerと同様に動作します。

あるいは、次のようなものです:

int main (void)
{
    // will print out debug info every time foo() is called
    register_stack_trace_function(foo); 

    // etc...
}

register_stack_trace_functionは、 fooが呼び出されるたびにスタックトレースを出力する何らかの内部ブレークポイントを設定します。

このようなものは、いくつかの標準Cライブラリに存在しますか?

私はLinuxでGCCを使って作業しています。

バックグラウンド

私は、この動作に影響を与えるべきではないコマンドラインスイッチに基づいて動作が異なるテストを実行しています。 私のコードは疑似乱数ジェネレータを持っていますが、これらのスイッチに基づいて別々に呼び出されていると想定しています。 私はスイッチの各セットでテストを実行し、乱数ジェネレータがそれぞれ異なる方法で呼び出されるかどうかを確認したいと考えています。


特定の関数が呼び出されるたびにCまたはC ++の実行中のプロセスにコールスタックをダンプする方法はありますか?

いいえ、プラットフォームに依存するソリューションが存在する可能性があります。


特定の関数が呼び出されるたびにCまたはC ++の実行中のプロセスにコールスタックをダンプする方法はありますか?

特定の関数でreturn文の代わりにマクロ関数を使用することができます。

たとえば、returnを使用する代わりに、

int foo(...)
{
    if (error happened)
        return -1;

    ... do something ...

    return 0
}

マクロ関数を使用することができます。

#include "c-callstack.h"

int foo(...)
{
    if (error happened)
        NL_RETURN(-1);

    ... do something ...

    NL_RETURN(0);
}

ある関数でエラーが発生すると、次のようなJavaスタイルのコールスタックが表示されます。

Error(code:-1) at : so_topless_ranking_server (sample.c:23)
Error(code:-1) at : nanolat_database (sample.c:31)
Error(code:-1) at : nanolat_message_queue (sample.c:39)
Error(code:-1) at : main (sample.c:47)

完全なソースコードがここにあります。

c-callstack(https://github.com/Nanolat)


Linux専用のソリューションでは、 void *配列を返すbacktrace(3)を使用することができます(実際には、それぞれが対応するスタックフレームからの戻りアドレスを指しています)。 これらを使用目的に変換するには、 backtrace_symbols(3)ます。

backtrace(3)のnotesセクションに注意してください:

シンボル名は、特別なリンカオプションを使用しないと使用できない場合があります。 GNUリンカを使用するシステムでは、-rdynamicリンカオプションを使用する必要があります。 静的関数の名前は公開されておらず、バックトレースでは使用できないことに注意してください。


これにPoppyを使うことができます。 これは通常、クラッシュ中にスタックトレースを収集するために使用されますが、実行中のプログラムに対してもスタックトレースを出力することができます。

ここでは良い部分があります:スタック上の各関数の実際のパラメータ値、さらにローカル変数、ループカウンタなどを出力することができます


もちろん、次の質問です:十分でしょうか?

スタックトレースの主な欠点は、デバッグに非常に便利な引数の値のような、他に何も持っていない正確な関数がある理由です。

gccとgdbにアクセスできない場合は、 assertを使用して特定の条件をチェックし、条件が満たされていない場合はメモリダンプを生成することをお勧めします。 もちろん、これはプロセスが停止することを意味しますが、単なるスタックトレースの代わりに本格的なレポートを作成できます。

あまり目立たない方法を望むなら、いつでもロギングを使うことができます。 例えば、 Pantheiosような非常に効率的な伐採施設があります。 何が起こっているのか、もう一度、あなたにもっと正確なイメージを与えることができます。


古いスレッドへの別の答え。

これを行う必要があるときは、通常はsystem()pstack

だからこのようなもの:

#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <sstream>
#include <cstdlib>

void f()
{
    pid_t myPid = getpid();
    std::string pstackCommand = "pstack ";
    std::stringstream ss;
    ss << myPid;
    pstackCommand += ss.str();
    system(pstackCommand.c_str());
}

void g()
{
   f();
}


void h()
{
   g();
}

int main()
{
   h();
}

この出力

#0  0x00002aaaab62d61e in waitpid () from /lib64/libc.so.6
#1  0x00002aaaab5bf609 in do_system () from /lib64/libc.so.6
#2  0x0000000000400c3c in f() ()
#3  0x0000000000400cc5 in g() ()
#4  0x0000000000400cd1 in h() ()
#5  0x0000000000400cdd in main ()

これは、Linux、FreeBSD、Solaris上で動作するはずです。 私はmacOSがpstackや単純な同等の機能を持っているとは思わないが、このスレッドには代替手段があるようだ


自分で機能を実装することができます:

グローバル(文字列)スタックを使用し、各関数の先頭に関数名とそのような他の値(例えばパラメータ)をこのスタックにプッシュします。 関数の終了時に再びポップします。

呼び出し時にスタックの内容を出力する関数を記述し、コールスタックを表示する関数でこれを使用します。

これは多くの作業のように聞こえるかもしれませんが、非常に便利です。





callstack