2009-06-11 18 views
27

最初に調整するのが最も難しいことの1つは、Cでpthreadsを使用した最初の激しい経験プログラミングでした。次のコード行が大部分であることを正確に知ることに慣れていました私のデバッグテクニックは、その期待に集中していました。Cプログラミング:pthreadを使用したデバッグ

Cのpthreadを使ってデバッグするには、どのような良いテクニックがありますか?ツール、ツール、またはデバッグに役立つツールを追加することなく、個人的な方法論を提案することができます。

P.S.私はLinuxでgccを使ってCのプログラミングをしますが、必ずしもあなたの答えを抑えることはできません。

+3

+1これはたくさんの「AHA」を持つ素晴らしい質問だと思う - 潜在的 – tr9sh

答えて

29

Valgrindは、競合状態とpthreads APIの誤用を見つける優れたツールです。プログラムメモリ(およびおそらく共有リソース)のモデルにアクセスし、バグが良性であっても欠落しているロックを検出します(当然、後の時点では予期せず良性になりません)。

これを使用するには、valgrind --tool=helgrindhere is its manualを呼び出します。また、valgrind --tool=drdmanual)があります。 HelgrindとDRDは異なるモデルを使用しているため、重複している可能性のあるバグを検出します。偽陽性も起こる可能性があります。

とにかく、valgrindは私のために(しかし、すべてではありません)無数の時間のデバッグを保存しました。マルチスレッドのデバッグに

+0

+1 Valgrindの場合 –

+1

はい、偽陽性を無視するには--suppressionsファイルを使用します。これは特にC++ STLを使用している場合に必要です。 – rleir

+1

私のお尻を保存したこのポスト!!!!!私はプログラムをデバッグすることができずに何日も過ごしていました。このポストを見つけた後、私は時間内に問題を解決しました!ありがとうございました! – user381261

2

マルチスレッドアプリケーションのデバッグは難しいです。 * nix環境用のGDB(オプションのDDDフロントエンド付き)や、Windows上でVisual Studioに付属しているような良いデバッガは、大いに役立ちます。

+2

DDDはgdb(と他のデバッガ)の単なるグラフィカルフロントエンドです。実際にはデバッガ自体ではありません –

+1

そうです。修正されました。 – luke

0

私は多くのブレークポイントを使用する傾向があります。実際にスレッド関数を気にする必要はありませんが、副作用が気になるのであれば、それらをチェックする良いタイミングは、終了する直前か待機状態に戻ります。

7

スレッドプログラムのデバッグについて驚くべきことの1つは、バグの変更を見つけることが多く、printfを追加したり、デバッガでプログラムを実行するときに消えてしまうことです(口語:Heisenbug)。

スレッドプログラムでは、通常、Heisenbugはrace conditionを持つことを意味します。優れたプログラマは、順序に依存する共有変数やリソースを探します。泥棒プログラマーは、sleep()ステートメントで盲目的に修正しようとします。

1

私のアプローチは、シングルスレッドに似ていますが、より多くの時間は、通常は思考段階に費やされている:

  1. が問題を引き起こして何ができるかについての理論を開発します。

  2. 理論が真である場合、どのような結果が期待できるかを決定します。

  3. 必要に応じて、結果と理論を反証または検証できるコードを追加します。

  4. あなたの理論が正しい場合は、問題を解決してください。

理論を証明する「実験」は、疑わしいコードの周りにクリティカルセクションまたはミューテックスを追加することがよくあります。次に、クリティカルセクションを体系的に縮小することで、問題を絞り込むようにします。クリティカルセクションは必ずしも最良のフィックスであるとは限りません。しかし、彼らは '喫煙銃'を突き止めるのに便利です。

私が言ったように、シングルスレッドデバッグにも同じ手順が適用されますが、デバッガにジャンプして実行するのは非常に簡単です。マルチスレッドデバッグでは、コードの理解を深める必要があります。通常、デバッガを使用してマルチスレッドコードを実行しても何も役に立ちません。

また、hellgrindは素晴らしいツールです。インテルのスレッド・チェッカーは、Windowsでも同様の機能を果たしますが、hellgrindよりもコストがかかります。

2

「思考」段階では、コーディングを開始する前に、ステートマシンの概念を使用してください。それはデザインをはるかに明確にすることができます。

あなたのプログラムのダイナミクスを理解するのに役立ちます。しかし、彼らはソースコードを混乱させるので、マクロDEBUG_OUT()を使用し、その定義でブール値フラグを使用可能にします。さらに良いことに、 'kill -USR1'で送信するシグナルでこのフラグをセット/クリアしてください。出力をタイムスタンプ付きのログファイルに送信します。

また、assert()を使用して、gdbとdddを使用してコアダンプを解析することを検討してください。

0

私がマルチスレッドプログラミングを始めたとき、私はデバッガの使用をやめました。 私にとっては、プログラムの分解とカプセル化が重要なポイントです。

モニタは、エラーのないマルチスレッドプログラミングの最も簡単な方法です。 複雑なロックの依存関係を避けることができない場合は、サイクリックであるかどうかを確認するのは簡単です。 - プログラムがハングするまで待機し、 'pstack'を使用してスタックトレースをチェックします。 新しいスレッドと非同期通信バッファを導入することによって、循環ロックを解除できます。

アサーションを使用して、ソフトウェアの特定のコンポーネント用のシングルスレッドユニットテストを作成します。必要に応じてデバッガで実行できます。

1

私は排他的にマルチスレッドの高性能な世界で開発していますので、ここでは私が使用している一般的な習慣です。最高の最適化設計 -

は、より良いアルゴリズムです:

1)論理的に分離粉々にあなたの機能を分割します。これは、コールが「A」と「A」のみを実行することを意味します。つまり、AではなくBからCになります。
2)副次的な影響なし:すべてのネイキッドなグローバル変数を静的にまたは無しに廃止します。副作用を完全に取り除くことができない場合は、いくつかの場所に分離してください(コードに集中させてください)。
3)できるだけ多くの絶縁部品を再投入してください。これは、ステートレスであることを意味します。すべての入力を定数として受け取り、論理的に定数の宣言されたパラメータを操作して出力を生成します。可能な限り、参照の代わりに値渡し。
4)状態がある場合は、ステートレスサブアセンブリと実際の状態マシンを明確に分離します。理想的には、ステートマシンはステートレスコンポーネントを操作する単一の関数またはクラスになります。

デバッグ:

スレッディングバグが2つの幅広いflavors-競合やデッドロックに来る傾向にあります。原則として、デッドロックははるかに決定的です。

1)データが破損していますか?:YES =>おそらくレース。
2)毎回実行するか、いくつかの実行でバグが発生しますか?はい=>デッドロックの可能性があります(レースは一般的には非決定論的です)。
3)プロセスがハングアップしていますか?はい=>どこかにデッドロックがあります。時にはハングアップするだけの場合は、おそらくレースもあるでしょう。

ブレイクポイントは、論理的には類似しているため、コード内のTHEMSELVESと同じように動作することがよくあります。他のコンテキスト(あなた)が再開する信号を送信するまで、現在のコンテキストで実行を停止させます。つまり、コード内にブレークポイントを表示し、そのスレッドの振る舞いを変更する必要があります。ブレークポイントは競合状態に影響しますが、(一般的には)デッドロックは影響を受けません。

これは、すべてのブレークポイントを削除し、バグの種類を特定し、次に再導入して修正することを意味します。それ以外の場合は、単に物事をさらに歪ませます。

関連する問題