6

これはとても腹立たしいです!>_<

巨大で複雑な Haskell ライブラリを作成しました。私は小さなテスト プログラムを書きましたが、これまでのところ、クラッシュし続ける理由を突き止めるために約 8 時間費やしました。GHC は「奇妙なクロージャ タイプ」について不満を言うことがあります。時々、セグメンテーション違反が発生します。明らかに問題はメモリの破損です。

ライブラリ自体は 100% 純粋な Haskell です。ただし、テスト プログラムは、配列に関連するいくつかの安全でない GHC プリミティブを使用します。これが明らかに問題の原因です。実際、その行をコメントアウトするwriteArray#と、プログラムはクラッシュしなくなります。しかし、これは私の麺を完全に揚げています...私が知る限り、私が使用したすべての配列境界は完全に有効です。プログラムはそれらをすべて出力します。それらはすべて正で、配列サイズよりも小さいです。

最初のプログラムと同じことを行う 2 番目のプログラムを作成しましたが、巨大で複雑なライブラリは必要ありません。私は試してみましたが、まったくクラッシュさせることはできません。私が何をしてもクラッシュするようには見えませんが、実際の配列とほぼ同じことを行います。

他にトラブルシューティングのヒントはありますか? メモリが破損している正確な瞬間を追跡する方法はありますか? (システムが破損に気付いた瞬間ではありません。)


アップデート:

問題は何をしますか?

基本的には、ピクセル バッファーを表す配列を作成します。すべてのピクセルを反復処理し、対応する値を書き込む 1 つのスレッドを生成します。そして、配列を読み取る 2 番目のスレッドを生成し、かなり複雑なプロトコルを使用してピクセルをネットワーク ソケットに書き込みます。(したがって、私がテストしようとしている大きなライブラリ。)

ライター スレッドを生成しないと、クラッシュはなくなります。ライター スレッドで呼び出しをコメント アウトするwriteArray'と、クラッシュはなくなります。各ピクセルを書き込む前に、書き込みスレッドはピクセル座標配列インデックスを出力します。印刷されるものはすべて完璧に見えます。それでも... クラッシュが止まりません。

GHCの配列プリミティブがスレッドセーフか何かではないのだろうか。(何らかの違いが生じた場合に備えて、リーダー スレッドのように見える配列のコピーは安全に凍結されていませんが、ライター スレッドはそれを並行して変更し続けます。)

ただし、ネットワーク経由でトラフィックを送信せずに、まったく同じことを行うプログラムを作成しました。このプログラムは細部まで完璧に機能します。機能しないのは、本当に複雑なプログラムだけです。それはどれほど迷惑ですか?

これは動作します: http://hpaste.org/70987

これはそうではありません: http://hpaste.org/70988

4

6 に答える 6

6

安全でないことがわかっている機能を、安全でチェック済みのバージョンに置き換えます。結果として発生する例外がないかログを調べて、コードを修正します。

于 2012-07-06T13:41:45.223 に答える
6

おそらく、テストプログラムとライブラリを備えたプログラムの違いは、後者の場合、より多くの割り当てがあるため、GCがより頻繁に呼び出されることです。

リーダースレッドのように見える配列のコピーは安全ではありません-凍結されていますが、ライタースレッドは同時にそれを変更し続けています。

おそらくGCは、フリーズ後も可変配列が参照されていることを追跡できません。この場合、GCはフリーズした配列を移動する可能性がありますが、writeArray#は古いポインターを使用して書き込みを実行します。

于 2012-07-06T13:42:26.187 に答える
6

安全でないプリミティブの使用を既にログに記録しています。

これらのログを調べて不変条件の違反を探すプログラムを作成しましたか?

于 2012-07-06T12:24:46.620 に答える
5

修正される可能性があります:

何かを変更したところ、コードがクラッシュしなくなりました。それはまぐれかもしれませんし、私が「本当に」問題を解決したのかもしれません。言うのが難しい。

仮説:

問題は、同時スレッドから同じ配列の可変コピーと不変コピーを読み取っているようです。(簡略化されたテストはまさにこれを行い、クラッシュしません。)

関係のない不変配列からネットワークスレッドを読み取らせたところ、クラッシュが停止しました。ある変更可能な配列から別の新しい変更可能な配列にデータをコピーするループを追加したところ、新しいものが凍結されて検査されました。これは完全に機能するようです。

したがって、同じ配列の両方のバージョンへの同時アクセスを GHC が処理する際の不具合にすぎないようです。

(それか、まぐれです。プログラムで何かを変更したところ、クラッシュが停止し、再びクラッシュし始めたことが何度かあります...)

更新:これは完全に修正されたようです。この変更を行ってから、クラッシュすることはなくなりました。ここで私を助けてくれたすべての人に感謝します。:-)

于 2012-07-06T14:08:59.877 に答える
4

私が知っている唯一のことは、Debug.Trace を使用することです。

import Debug.Trace

debug = flip trace

その後

main = (1 + 2) `debug` "adding or whatever and whatnot (also can have code here)"
于 2012-07-06T10:54:52.240 に答える