3

私はvalgrind3.6.0を持っていますが、どこでも検索しても何も見つかりませんでした。

問題は、valgrindを使用しているときにfloat番号にアクセスしようとすると、セグメンテーションフォールトが発生することですが、valgrindを使用せずにプログラムをそのまま実行すると、すべてが期待どおりに実行されます。

これはコードの一部です:

class MyClass {
    public:
    void end() {
        float f;
        f = 1.23;
        std::stringstream ss;
        ss << f;
        std::cout << ss.str();
    }
};

extern "C" void clean_exit_on_sig(int sig) {
    //Code logging the error
    mc->end();
    exit(1);
}

MyClass *mc;
int main(int argc, char *argv[]) {
    signal(SIGINT , clean_exit_on_sig);
    signal(SIGABRT , clean_exit_on_sig);
    signal(SIGILL , clean_exit_on_sig);
    signal(SIGFPE , clean_exit_on_sig);
    signal(SIGSEGV, clean_exit_on_sig);
    signal(SIGTERM , clean_exit_on_sig);
    mc = new MyClass();
    while(true) {
        // Main program loop
    }
}

Control + Cを押すと、プログラムは信号を正しくキャッチし、すべてが正常に動作しますが、valgrindを使用してプログラムを実行すると、このコマンドを実行しようとするとss << f; // (Inside MyClass)、セグメンテーション違反がスローされます:-/

私もこれを試しました:

std::string stm = boost::lexical_cast<std::string>(f);

しかし、ブーストがフロート番号にアクセスしたときも、セグメンテーション違反のシグナルを受信し続けます。

これは、ブーストでセグメンテーション違反が発生したときのバックトレースです。

./a.out(_Z17clean_exit_on_sigi+0x1c)[0x420e72]
/lib64/libc.so.6(+0x32920)[0x593a920]
/usr/lib64/libstdc++.so.6(+0x7eb29)[0x51e6b29]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE15_M_insert_floatIdEES3_S3_RSt8ios_baseccT_+0xd3)[0x51e8f43]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecd+0x19)[0x51e9269]
/usr/lib64/libstdc++.so.6(_ZNSo9_M_insertIdEERSoT_+0x9f)[0x51fc87f]
./a.out(_ZN5boost6detail26lexical_stream_limited_srcIcSt15basic_streambufIcSt11char_traitsIcEES4_E9lcast_putIfEEbRKT_+0x8f)[0x42c251]
./a.out(_ZN5boost6detail26lexical_stream_limited_srcIcSt15basic_streambufIcSt11char_traitsIcEES4_ElsEf+0x24)[0x42a150]
./a.out(_ZN5boost6detail12lexical_castISsfLb0EcEET_NS_11call_traitsIT0_E10param_typeEPT2_m+0x75)[0x428349]
./a.out(_ZN5boost12lexical_castISsfEET_RKT0_+0x3c)[0x426fbb]
./a.out(This line of code corresponds to the line where boost tries to do the conversion)

これは、デフォルトの文字列ストリーム変換を使用したものです。

./a.out(_Z17clean_exit_on_sigi+0x1c)[0x41deaa]
/lib64/libc.so.6(+0x32920)[0x593a920]
/usr/lib64/libstdc++.so.6(+0x7eb29)[0x51e6b29]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE15_M_insert_floatIdEES3_S3_RSt8ios_baseccT_+0xd3)[0x51e8f43]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecd+0x19)[0x51e9269]
/usr/lib64/libstdc++.so.6(_ZNSo9_M_insertIdEERSoT_+0x9f)[0x51fc87f]
./a.out(This line of code corresponds to the line where I try to do the conversion)

a.outは私のプログラムであり、valgrindを次のように実行します。valgrind --tool=memcheck ./a.out

もう1つの奇妙なことはmc->end();、プログラムが正常に実行されているときに電話をかけると(信号を受信し、Objectが作業を終了したばかり)、(valgrindの場合と同様に)セグメンテーション違反が発生しないことです。

「Control+Cでプログラムを閉じないでください。何とか何とか...」このコードは、セグメンテーション違反の場合にデータを失うことなく、デッドロックのためにプログラムを強制終了することなく、プログラムで発生する可能性のあるエラーをログに記録するためのものです。または、他の何か。

編集:たぶんvalgrindのバグです(私は知りません、グーグルで検索しましたが何も見つかりませんでした、私を殺さないでください)、どんな回避策も受け入れられます。

EDIT2:ブーストがostreamも呼び出すことに気づきました(ここではvimを使用するよりも明確です:-/)、sprintffloat変換を試してみます。

EDIT3:これを試しましsprintf(fl, "%.1g", f);たが、それでもクラッシュします、バックトレース:

./a.out(_Z17clean_exit_on_sigi+0x40)[0x41df24]
/lib64/libc.so.6(+0x32920)[0x593a920]
/lib64/libc.so.6(sprintf+0x56)[0x5956be6]
./a.out(Line where sprintf is)
4

2 に答える 2

1

さて、数時間の読書と調査の後、私は問題を見つけました。誰も答えないので、私は自分の質問に答えるつもりです。@KerrekSBによるコメントだけです[ https://stackoverflow.com/users/596781/kerrek- sb ]ですが、コメントは受け付けません。(ありがとうございました)

シグナルハンドラー内と同じくらい簡単で、安全に一連の関数を呼び出すことしかできません:http: //pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html

非同期に安全でない関数を呼び出すと、それらは機能しますが、常に機能するとは限りません。

シグナルハンドラー内で非同期セーフでない関数を呼び出したい場合は、次のようにすることができます。

  • 2本のパイプを作成します。int pip1[2]; int pip2[2]; pipe(pip1); pipe(pip2);
  • 新しいスレッドを作成し、スレッドが最初のパイプからデータを受信するのを待機させますread(pip1[0], msg, 1);
  • シグナルハンドラーが呼び出されたら、writeasync-safe関数を使用して最初のパイプに書き込みますwrite(pip1[1], "0", 1);
  • 次に、信号を2番目のパイプで待機させます。read(pip2[0], msg, 1);
  • スレッドはウェイクアップし、彼がしなければならないすべての仕事(この場合はデータベースにデータを保存する)を実行し、その後、スレッドに2番目のパイプにデータを書き込ませますwrite(pip2[1], "0", 1);
  • これで、メインスレッドがウェイクアップし、_Exit(1)または他の何かで終了します。

情報:

2つのパイプを使用しているのは、パイプに書き込み、その直後にそれを読み取ると、メインスレッドがデータを読み取ったため、2番目のスレッドがウェイクアップしない可能性があるためです。また、2番目のスレッドがデータを保存しているときにメインスレッドを終了させたくないため、2番目のパイプを使用してメインスレッドをブロックしています。

共有リソースの変更中にシグナルハンドラーが呼び出された可能性があることに注意してください。2番目のスレッドがそのリソースにアクセスすると、2番目のセグメンテーション違反が発生する可能性があるため、2番目のスレッド(グローバル変数など)で共有リソースにアクセスするときは注意してください。 。

valgrindでテストしていて、信号を受信するときに「false」メモリリークを受信したくない場合は、終了する前に、の代わりにこれを行うことがpthread_join(2ndthread, NULL)できexit(1)ます_Exit(1)。これらは非同期セーフではない機能ですが、少なくともメモリリークをテストし、「false」メモリリークを受信せずに信号でアプリを閉じることができます。

これが誰かを助けることを願っています。@KerrekSBに改めて感謝します。

于 2012-09-13T19:43:38.290 に答える
0

デバッガーなどは、通常は取得しないプロセスにシグナルを送信することがあります。たとえば、recvを使用してgdbで動作する関数を変更する必要がありました。信号を使用する前に、信号が何であるかを確認し、mcがnullでないことを確認してください。それがあなたを答えに近づけ始めるかどうか見てください。

おそらく、new(または他の何か)を使用すると、mcが初期化される前にvalgrindがハンドラーによってキャッチされているシグナルを送信している可能性があると思います。

また、end()関数を公開せずに「class」を使用するとコンパイルされないため、実際のコードを貼り付けなかったことも明らかです。

于 2012-09-12T23:25:48.347 に答える