7

Win32 アプリケーションのハングの原因を特定できません。ソフトウェアは、いくつかのデータをタイトなループで OpenGL ビジュアルにレンダリングします。

std::vector<uint8_t> indices;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_DOUBLE, 0, vertexDataBuffer);
while (...) {
    // get index type (1, 2, 4) and index count
    indices.resize(indexType * count);

    // get indices into "indices" buffer
    getIndices(indices.data(), indices.size()); //< seems to hang here!

    // draw (I'm using the correct parameters)
    glDrawElements(GL_TRIANGLES_*, count, GL_UNSIGNED_*);
}
glDisableClientState(GL_VERTEX_ARRAY);

このコードは、VC11 Update 1 (CTP 3) を使用してコンパイルされています。getIndices()最適化されたバイナリを実行すると、これらのループのいくつかの後に (これについては以下で詳しく説明します)への呼び出し内でハングします。私は既に持っています...

  • すべてのバッファをトリプル検証し、CRC を追加して、バッファ オーバーランが発生していないことを確認しました
  • ヒープが破損していないことを確認するために、ループ内にHeapValidate()への呼び出しを追加しました
  • 使用された ApplicationVerifier
  • GFlags と PageHeapを使用してヒープ割り当ての監視を有効にしました。
  • アプリケーションがロックアップしたときに WinDbg に侵入した

割り当てられたバッファにアクセスするコードに問題はなく、ヒープの破損もありませんでしたただし、断片化の少ないヒープを無効にすると、問題は解消されます。indicesバッファに別の(断片化の少ない)ヒープを使用すると、それも消えます。

とにかく、デッドロックにつながるスタックトレースは次のとおりです。

0:000> kb
ChildEBP RetAddr  Args to Child              
0034e328 77b039c3 00000000 0034e350 00000000 ntdll!ZwWaitForKeyedEvent+0x15
0034e394 77b062bc 77b94724 080d36a8 0034e464 ntdll!RtlAcquireSRWLockExclusive+0x12e
0034e3c0 77aeb652 0034e464 0034e4b4 00000000 ntdll!RtlpCallVectoredHandlers+0x58
0034e3d4 77aeb314 0034e464 0034e4b4 77b94724 ntdll!RtlCallVectoredExceptionHandlers+0x12
0034e44c 77aa0133 0034e464 0034e4b4 0034e464 ntdll!RtlDispatchException+0x19
0034e44c 77b062c5 0034e464 0034e4b4 0034e464 ntdll!KiUserExceptionDispatcher+0xf
0034e7bc 77aeb652 0034e860 0034e8b0 00000000 ntdll!RtlpCallVectoredHandlers+0x61
0034e7d0 77aeb314 0034e860 0034e8b0 0034ec28 ntdll!RtlCallVectoredExceptionHandlers+0x12
0034e848 77aa0133 0034e860 0034e8b0 0034e860 ntdll!RtlDispatchException+0x19
0034e848 1c43c666 0034e860 0034e8b0 0034e860 ntdll!KiUserExceptionDispatcher+0xf
0034ebe8 1c43c4e5 0034ec28 080d35d0 080d35d6 lcdb4!lc::db::PackedIndices::unpackIndices<unsigned char>+0x86
0034ec14 1c45922d 0034ec28 080d35d0 00000006 lcdb4!lc::db::PackedIndices::unpack+0xb5
...
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx getIndices

lc::db::PackedIndices::unpackIndices()完全を期すために、デバッグ用に追加されたすべてのコードを含む のコードをhttp://ideone.com/sVVXX7に投稿しました。

への呼び出しをトリガーするコードKiUserExceptionDispatcher(*p++) = static_cast<T>(index);( mov dword ptr [esp+10h],eax) です。

何が起こっているのか理解できないようです。例外がスローされたようですが、例外ハンドラが呼び出されません。アプリケーションがハングするだけです。デッドロックされたクリティカル セクション ( !lock) をチェックしましたが、何も見つかりませんでした。さらに、メモリの場所はすべて有効であるため、例外が発生する理由がわかりません。誰かヒントをくれませんか?

アップデート

スローされている例外のタイプを見つけようとしました:

0:000> s -d esp L1000 1003f
0028ebdc  0001003f 00000000 00000000 00000000  ?...............
0028efd8  0001003f 00000000 00000000 00000000  ?...............
0:000> .cxr 0028ebdc
eax=77b94724 ebx=0804be30 ecx=00000002 edx=00000004 esi=77b94724 edi=0804be28
eip=77b062c5 esp=0028eec4 ebp=0028eee4 iopl=0         nv up ei ng nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010287
ntdll!RtlpCallVectoredHandlers+0x61:
77b062c5 ff03            inc     dword ptr [ebx]      ds:002b:0804be30=00000001
0:000> .cxr 0028efd8
eax=0000003b ebx=00000001 ecx=0804bd98 edx=0028f340 esi=0028f340 edi=04b77580
eip=1c43c296 esp=0028f2c0 ebp=0028f2fc iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
lcdb4!lc::db::PackedIndices::unpackIndices<unsigned char>+0x36:
1c43c296 8801            mov     byte ptr [ecx],al          ds:002b:0804bd98=3e
4

3 に答える 3

2

スレッドは、OS例外処理コードに属するSRWの排他的ロック(スリム読み取り/書き込みロック)を待ってハングしています。そして、その例外はあなたのコードによって引き起こされます。正確な例外とその詳細は、次のスタックフレームを使用して見つけることができます。0034e848 77aa0133 0034e860 0034e8b0 0034e860 ntdll!RtlDispatchException+0x19 -RtlDispatchExceptionへの引数EXCEPTION_RECORDへのポインターです。したがって、.exr 0034e860と入力すると、例外レコードが表示されます。例外レコードから、どのアドレスへのアクセスが例外を引き起こしているかがわかります(例外がアクセス違反例外の場合)。

これらの手順の後、ヒープに正しく割り当てたアドレスへの書き込みが原因でアクセス違反が発生していることがわかりました。コマンド!address "を使用して、そのアドレスを含む仮想ページの保護属性を見つけることができます。仮想アドレス」

これらのヒープアドレスでページ保護属性が(コードによって)PAGE_READONLYに変更されていることがわかったので、他のスレッドの呼び出しスタックを確認した後、根本的な原因を見つけるのに役立つと思われる次の推測があります。

Windowsヒープマネージャは、ヒープの破損を示す例外を発生させる前に、ページ属性を変更していると思います。あなたが示した他のスレッドのコールスタックから、oleヒープにもいくつかの破損があるようです。問題の根本は、おそらくヒープを破壊するコードです-ヒープはその後に検出され、例外を発生させます。その後、OSの例外メカニズム実装コードが起動し、呼び出す前にSWRロックにハングアップしますまたは他のライブラリコードの例外ハンドラ。これに続いて、コード内の別の無知なスレッドがヒープメモリに正しく接触します。ヒープメモリは、すでに検出された破損のためにすでに保護されており、例外が発生し、例外メカニズムコードが起動して同じものに分類されます。デッドロック。

于 2012-11-04T05:49:05.570 に答える
2

スタック トレースが物語っています。プログラムがクラッシュしています。これはアクセス違反の例外である可能性が高く、C++ コードの典型的な障害モードであり、通常はヒープの破損によって引き起こされます。次に、Windows は例外フィルターを呼び出して、例外を処理するコードを探します。最初に、AddVectoredExceptionHandler() によってインストールされるハンドラーがあります。これらのハンドラーのいずれかがクラッシュを引き起こしたときに再エントリを防ぐために、ロックを取得する必要があります。

そして、それがお金が止まるところです。正確な理由は、スタック トレースからは不明です。別のスレッドもヒープの破損に失敗し、例外の処理で忙しく、ロックを取得したことが原因である可能性があります。Debug + Windows + Threads を使用してそれらを確認します。しかし、より可能性が高いのは、プロセスの状態が非常に壊れているため、ロック オブジェクト自体も破損している可能性があります。可能性は低いですが、実際に起こります。

そして、はい、断片化の少ないヒープをオフにすると、ヒープの破損を隠すコツがあります。メモリ レイアウトは非常に異なるため、破損の原因となっているコードが何であれ、問題のないものを攻撃した可能性があります。もちろん、それは解決策ではありません。

Debug + Exception、「Win32 Exceptions」のThrownチェックボックスにチェックを入れます。例外がスローされるとデバッガーが停止するようになりました。少なくとも、どの例外がスローされているかがわかります。最終的には、ヒープの破損が発生した場所を特定する必要があります。クラッシュしたコードに配置されることはありません。頑張ってデバッグしてください。

于 2012-11-02T12:04:50.217 に答える
1

ATI グラフィック カード (ATI ドライバーを使用) を使用している場合、状態をリークしてはならないという既知の問題があります。そうしないと、後でメモリの破損が発生します。

できるすべての状態を無効にしてみて (glDisableClientState)、APITrace を使用して忘れた状態を見つけてください。

グラフィック ドライバのメモリ破損をテストする簡単な方法の 1 つは、別のボード/ドライバでテストするか、ソフトウェア レンダリングを強制することです。

于 2012-11-02T11:20:49.787 に答える