web-dev-qa-db-ja.com

ランダムクラッシュの解決

C++アプリケーションでランダムクラッシュが発生します。1か月間はクラッシュせず、1時間に10回クラッシュする可能性があります。また、起動時にクラッシュすることもあれば、数時間の操作後にクラッシュすることもあります(またはすべて)。

GNU/LinuxではGCC、WindowsではMingWを使用しているため、Visual Studio JITデバッグを使用できません...

どうすればよいかわからず、コードをランダムに見ても機能しません。コードは巨大です(そして、かなりの部分が私の作業ではなかったため、かなりの量のレガシーなものが含まれています)。クラッシュを再現する方法についての手がかりを持っています。

編集:多くの人々がそれについて言及しました...どのようにしてコアダンプ、ミニダンプまたはwhateverdumpを作成しますか?事後デバッグが必要なのはこれが初めてです。

EDIT2:実際、DrMingwはコールスタックをキャプチャしましたが、メモリ情報はありません...残念ながら、コールスタックはあまり役に立ちませんでした。最後の方で突然、デバッグ情報を持たないライブラリ(または何か)に入るからです。 、一部の16進数のみが表示されます...したがって、さらに詳細な情報を提供する適切なダンプが必要です(特にメモリの内容について...具体的には、「アクセス違反」エラーを発生させた場所の内容)。

また、私のアプリケーションはLuaとLuabindを使用しています。おそらく、エラーは.luaスクリプトが原因で発生していますが、それをデバッグする方法がわかりません。

35
speeder

試してみてください Valgrind (無料、オープンソースです):

現在、Valgrindディストリビューションには、メモリエラー検出器、2つのスレッドエラー検出器、キャッシュおよびブランチ予測プロファイラー、コールグラフ生成キャッシュプロファイラー、およびヒーププロファイラーという6つの本番品質のツールが含まれています。また、ヒープ/スタック/グローバルアレイオーバーラン検出器とSimPoint基本ブロックベクトルジェネレーターの2つの実験ツールも含まれています。 X86/Linux、AMD64/Linux、PPC32/Linux、PPC64/Linux、およびX86/Darwin(Mac OS X)で動作します。

Valgrindよくある質問

パッケージの Memcheck 部分は、おそらく開始する場所です:

Memcheckはメモリエラー検出器です。 CおよびC++プログラムに共通する次の問題を検出できます。

  • すべきでないメモリへのアクセス。ヒープブロックのオーバーランおよびアンダーラン、スタックの最上位のオーバーラン、解放後のメモリへのアクセス。

  • 未定義の値、つまり初期化されていない値、または他の未定義の値から派生した値の使用。

  • ヒープブロックの二重解放などのヒープメモリの不正な解放、またはmalloc/new/new []とfree/delete/delete []の使用の不一致

  • Memcpyおよび関連する関数でのsrcおよびdstポインターの重複。

  • メモリリーク。

29
Mitch Wheat

最初に、あなたのプロセスが短期間に何度もクラッシュするのは幸運です。これで作業が簡単になります。

これがあなたの進め方です。

  • クラッシュダンプを取得する
  • 疑わしい機能のセットを分離する
  • 状態チェックを強化
  • 繰り返す

クラッシュダンプを取得

まず、本当にクラッシュダンプを取得する必要があります。

クラッシュしたときにクラッシュダンプを取得できない場合は、信頼できるクラッシュダンプを生成するテストを作成することから始めます。

デバッグシンボルを使用してバイナリを再コンパイルするか、デバッグシンボルを使用してクラッシュダンプを分析できることを確認します。

疑わしい機能を見つける

クラッシュダンプがあるとしたら、gdbまたはお気に入りのデバッガーでそれを見て、すべてのスレッドを表示することを忘れないでください!バグがあるのは、gdbに表示されるスレッドではない可能性があります。

バイナリがクラッシュしたとgdbが言う場所を見て、問題の原因になると思われる関数のセットを分離します。

複数のクラッシュを確認し、すべてのクラッシュで一般的にアクティブなコードセクションを分離することで、時間を節約できます。

締め付け状態チェック

クラッシュは通常、一貫性のない状態が原因で発生します。最善の方法は、州の要件を厳しくすることです。これは次の方法で行います。

問題を引き起こす可能性があると思われる関数ごとに、関数への入り口で入力またはオブジェクトが持つ必要のある正当な状態を文書化します。 (関数の終了時に必要な法的状態についても同じことを行いますが、それほど重要ではありません)。

関数にループが含まれている場合は、各ループ反復の開始時に必要な法的状態を文書化します。

そのような法的状態の表現すべてにアサートを追加します。

繰り返し

その後、プロセスを繰り返します。それでもアサートの外側でクラッシュする場合は、アサートをさらに厳しくします。ある時点で、プロセスはアサートでクラッシュしますが、ランダムなクラッシュが原因ではありません。この時点で、プログラムが関数に入ったときに正当な状態から、アサートが発生した時点で不正な状態に移行した原因を解明することに集中できます。

アサートと詳細なログを組み合わせると、プログラムの動作を理解しやすくなります。

15
user239558

他のすべてが失敗した場合(特に、デバッガーでのパフォーマンスが許容できない場合)は、広範なロギング。エントリポイントから始めます-アプリはトランザクションですか?入ってくる各トランザクションを記録します。キーオブジェクトのすべてのコンストラクター呼び出しを記録します。クラッシュは非常に断続的であるため、毎日呼び出されない可能性があるすべての関数の呼び出しをログに記録します。

少なくともcouldがクラッシュする場所の絞り込みを開始します。

14
Nicholas Knight

私が作業している場合、クラッシュするプログラムは通常、windbgにロードできるコアダンプファイルを生成します。

次に、プログラムがクラッシュしたときのメモリのイメージを取得します。それでできることは何もありませんが、少なくとも最後の呼び出しスタックが得られます。クラッシュした関数がわかったら、少なくとも問題を再現可能なテストケースに減らすことができれば、問題を追跡できる可能性があります。

8
ereOn

デバッガーの下でプログラムを開始し(GCCおよびMingWと一緒にデバッガーがあると確信しています)、デバッガーの下でクラッシュするまで待ちます。クラッシュした時点で、どの特定のアクションが失敗しているかを確認し、アセンブリコード、レジスタ、メモリ状態を調べることができます。これは、多くの場合、問題の原因を見つけるのに役立ちます。

8
sharptooth

プログラムがメモリ破損の影響を受けているようです。すでに述べたように、Linuxでの最良のオプションはおそらくvalgrindです。ただし、他に2つのオプションがあります。

  • まず最初に debug malloc を使用します。ほとんどすべてのCライブラリは、メモリを初期化するデバッグmalloc実装を提供し(通常のmallocは「古い」コンテンツをメモリに保持します)、割り当てられたブロックの境界の破損をチェックします。そして、それが十分でない場合、サードパーティの実装の幅広い選択肢があります。

  • VMWare Workstationをご覧になることをお勧めします。私はそのように設定していませんが、マーケティング資料から、かなり興味深いデバッグ方法をサポートしています。「記録」仮想マシンでデバッグ対象を実行します。メモリ破損が発生した場合は、破損したアドレスにメモリブレークポイントを設定し、VMにturn back timein the厳密にそのメモリの一部が上書きされた瞬間。Linux/ gdbを使用してリプレイデバッグをセットアップする方法については このPDF を参照してください。Workstation7には15日または30日のデモがあり、これで十分です。あなたのコードからそれらのバグを取り除く。

6
froh42

これらの種類のバグは常にトリッキーです-エラーを再現できない限り、追加情報がログに記録されるようにアプリケーションに変更を加え、エラーが再び発生するまで待つことが唯一の選択肢です。

Process Dumper と呼ばれる優れたツールがあり、これを使用して、例外が発生したり、予期せず終了したプロセスのクラッシュダンプを取得できます。ユーザーにインストールを依頼して、アプリケーションのルールを構成するように依頼できます。

あるいは、ユーザーに他のアプリケーションをインストールするように依頼したくない場合は、アプリケーションの例外を監視し、 MiniDumpWriteDump を呼び出してダンプ自体を作成することができます。

もう1つのオプションはロギングを改善することですが、(すべてをログに記録するだけでなく)ログに記録する情報を理解するのは難しい場合があるため、crash-問題を突き止めるためにロギングを変更する

私が言ったように、これらの種類のバグは常に診断するのが難しいです-私の経験では、それは通常、突然までログとクラッシュダンプを何時間もピアリングすることを含みますあなたはすべてが理にかなっているそのユーレカの瞬間を得る-キーは正しい情報を収集することです。

6
Justin

Linuxのvalgrindでアプリケーションを実行して、メモリエラーを探します。ランダムクラッシュは通常、メモリの破損に起因します。

Valgrindのmemcheckツールで見つけたすべてのエラーを修正すると、うまくいけばクラッシュは解消されます。

プログラム全体がvalgrindで実行するのに時間がかかりすぎる場合は、機能を単体テストに分割し、valgrindでthoseを実行すると、問題の原因となっているメモリエラーが見つかるはずです。

そうでない場合は、コアダンプが有効になっていることを確認してください(ulimit -a)がクラッシュした場合、gdbを使用して場所を見つけることができます。

4
Douglas Leeder

Linuxでこれを処理する方法はすでに聞いたことがあります。コアダンプを調べて、valgrindでコードを実行してください。したがって、最初のステップは、Linuxでエラーを見つけて、mingwでエラーが消えるかどうかを確認することです。ここでは誰もmudflapについて言及しなかったので、それを行います:Linuxディストリビューションで提供されている場合は、mudflapを使用します。 mudflapは、ポインターが実際に指すことを許可されている場所の情報を追跡することにより、ポインターの誤用とバッファーオーバーフローをキャッチするのに役立ちます。

そしてWindowsの場合:DrMingwと呼ばれるmingw用のJITデバッガがあります:

4

競合状態のようにトリッキーなことのように聞こえます。

デバッグビルドを作成して使用することをお勧めします。また、プログラムがクラッシュしたときにコアダンプが作成されることを確認する必要があります。

次にプログラムがクラッシュしたときに、コアダンプでgdbを起動して、問題の場所を確認できます。それは恐らく連続した障害でしょうが、これはあなたが始められるはずです。

3
fhd

私が最初に行うことは、gdb(WindowsとLinuxの両方)でコアダンプをデバッグすることです。 2つ目は、Lint、Prefast(Windows)、 Clang Analyzer などのプログラムまたは他の静的分析プログラム(多くの誤検知に備える)を実行することです。 3つ目は、Valgrind(またはその類似バリアント)、 Microsoft Application Verifier 、または Google Perftools のような、ある種のランタイムチェックです。

そしてロギング。ディスクに移動する必要はありません。たとえば、グローバルstd::list<std::string>これは、最後の100エントリまで剪定されます。例外がキャッチされると、そのリストの内容が表示されます。

3
Max Lybbert
  1. ロギングを開始します。コードが不安定だと思う場所にロギングステートメントを配置します。コードのテストに焦点を当て、問題をモジュールまたは関数に絞り込むまで繰り返します。

  2. どこにでもアサートを置く!

  3. あなたがそれにいる間、1つの式だけをアサートに入れてください。

  4. 失敗していると思われるコードの単体テストを記述します。これにより、ランタイム環境の他の部分から分離してコードを実行できます。

  5. 問題のあるコードを実行する自動テストをさらに記述します。

  6. 失敗している不良コードの上にコードを追加しないでください。それはばかげた考えです。

  7. ミニダンプを書き出す方法と、事後デバッグを行う方法を学びます。ここの他の人々はそれをかなりよく説明しているようです。

  8. バグを特定できるように、可能な限りさまざまな方法で不良コードを実行します。

  9. デバッグビルドを使用します。可能であれば、デバッガの下でデバッグビルドを実行します。

  10. 可能であれば、バイナリ、モジュールなどを削除してアプリケーションをトリミングし、バグを再現しやすくするようにします。

3
C Johnson

ここには良い答えがたくさんありますが、Luaの角度についてはまだ誰も触れていません。

Luaは一般的にかなりうまく動作しますが、メモリの破損やクラッシュの原因となる可能性があります。 Luaスタックがオーバーフローまたはアンダーフローするか、不正なバイトコードが実行されます。

このような多くのエラーを検出する簡単な方法の1つは、luaconf.hでlua_assertマクロを定義することです。これを定義すると(標準Cのアサートなど)、Luaコア内のさまざまな健全性チェックが有効になります。

2
tehtmi

別の基本的なチェック:プロジェクトの完全な再構築を行うを確認してください。さまざまなファイル(特にヘッダーファイル)を調整し、部分的なビルドを行っている場合、ビルドの依存関係が完全でないと、事態が複雑になる可能性があります。完全なリビルドはその可能性を取り除くだけです。

また、Windowsについては、Microsoftの Windowsのデバッグツール 、特に gflags ツールを確認してください。

1
Pierz

おそらく何らかの方法で割り当てられていないスペースにいくつかの値を配置するメモリエラーが発生しました。これはランダムクラッシュの良い理由です。長い間、誰もそのメモリを使用しようとしないため、エラーは発生しません。メモリを割り当て、ポインタを頻繁に使用する場所を確認する場所。これ以外に、他の人が指摘したように、画面とファイルの両方で、広範なロギングを使用する必要があります。

1
LostMohican

アプリケーションがWindows固有でない場合は、Linuxなどの別のプラットフォーム(異なるディストリビューション、32/64ビットなど)でプログラムをコンパイルして実行してみてください。それはあなたのプログラムのバグを引き起こすのを助けるかもしれません。もちろん、gdb、valgrindなどの他の投稿で言及されているツールを使用する必要があります。

0
tofu

さらに2つのポインター/アイデア(Linuxのコアダンプとvalgrind以外):

1)Nokiaの「Qt Creator」を試してください。それはmingwをサポートし、事後デバッガとして機能することができます。

2)可能であれば、アプリケーションを常にgdbで実行するだけでしょうか?

0
Frank Osterfeld