web-dev-qa-db-ja.com

マネージヒープの破損をデバッグする方法

私のプログラムは、catch(Exception e)ブロックでは処理できないエラーをスローし、クラッシュします。

アクセス違反破損状態例外。

これは奇妙なことです。私が知っているように、破損した状態の例外はアンマネージコードからスローされますが、ここでは StringBuilder メソッドを呼び出すときにこの例外が発生します。

コードはバックグラウンドスレッドで実行され、ときどきクラッシュし、簡単に再現することはできません。だから私はプロセスに WinDbg をアタッチし、例外の次のスタックを持っています:

_000000001dabd8c8 000007feea129a1d [HelperMethodFrame: 000000001dabd8c8]
000000001dabda00 000007fee90cfce8 System.Text.StringBuilder.ExpandByABlock(Int32)
000000001dabda40 000007fee90cfba4 System.Text.StringBuilder.Append(Char*, Int32)
000000001dabdaa0 000007fee9102955 System.Text.StringBuilder.Append(System.String, Int32, Int32)
000000001dabdaf0 000007ff00bf5ce3 MineUtils.Common.Strings.Strings.Replace(System.String, System.String, System.String, Boolean, Boolean)
000000001dabdb90 000007ff00bf5a59 MineUtils.Common.Strings.Strings.RemoveSubstrings(System.String, System.String, System.String, Boolean) [D:\Programs\Visual Studio 2005 Projects\MineUtils.Common\Strings\Strings.Common-Main.cs @ 1481
_

WinDbgは、この例外が発生したことを示しています。

_EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 000007feea129a1d (clr!WKS::gc_heap::find_first_object+0x0000000000000092)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000000000003d80
Attempt to read from address 0000000000003d80
_

そのような例外はメソッド属性[HandleProcessCorruptedStateExceptions]で処理できると読みましたが、StringBuilderのみを使用しているのにこの例外が発生するのはなぜですか?

これは以前のWinDbg分析です(StringBuilder.ToString()は例外を引き起こします):

_*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

FAULTING_IP:
clr!WKS::gc_heap::find_first_object+92
000007fe`ea129a1d f70100000080    test    dword ptr [rcx],80000000h

EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 000007feea129a1d (clr!WKS::gc_heap::find_first_object+0x0000000000000092)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000001
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000000000001c98
Attempt to read from address 0000000000001c98

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  0000000000001c98

READ_ADDRESS:  0000000000001c98

FOLLOWUP_IP:
clr!WKS::gc_heap::find_first_object+92
000007fe`ea129a1d f70100000080    test    dword ptr [rcx],80000000h

MOD_LIST: <ANALYSIS/>

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

MANAGED_STACK:
(TransitionMU)
000000001AB7DFC0 000007FEE90CFE07 mscorlib_ni!System.Text.StringBuilder.ToString()+0x27
000000001AB7E010 000007FF00C750A9 SgmlReaderDll!Sgml.Entity.ScanToken(System.Text.StringBuilder, System.String, Boolean)+0x169
000000001AB7E080 000007FF00C760E6 SgmlReaderDll!Sgml.SgmlDtd.ParseParameterEntity(System.String)+0xc6
000000001AB7E0F0 000007FF00C76FD8 SgmlReaderDll!Sgml.SgmlDtd.ParseModel(Char, Sgml.ContentModel)+0x298
000000001AB7E160 000007FF00C7701C SgmlReaderDll!Sgml.SgmlDtd.ParseModel(Char, Sgml.ContentModel)+0x2dc
000000001AB7E1D0 000007FF00C7701C SgmlReaderDll!Sgml.SgmlDtd.ParseModel(Char, Sgml.ContentModel)+0x2dc
000000001AB7E240 000007FF00C76BA5 SgmlReaderDll!Sgml.SgmlDtd.ParseContentModel(Char)+0x65
000000001AB7E290 000007FF00C763D7 SgmlReaderDll!Sgml.SgmlDtd.ParseElementDecl()+0xe7
000000001AB7E320 000007FF00C747A1 SgmlReaderDll!Sgml.SgmlDtd.Parse()+0xc1
000000001AB7E370 000007FF00C73EF5 SgmlReaderDll!Sgml.SgmlDtd.Parse(System.Uri, System.String, System.IO.TextReader, System.String, System.String, System.Xml.XmlNameTable)+0x175
000000001AB7E410 000007FF00C73B33 SgmlReaderDll!Sgml.SgmlReader.LazyLoadDtd(System.Uri)+0x163
000000001AB7E480 000007FF00C737B9 SgmlReaderDll!Sgml.SgmlReader.OpenInput()+0x19
000000001AB7E4E0 000007FF00C7334C SgmlReaderDll!Sgml.SgmlReader.Read()+0x1c
000000001AB7E530 000007FEE5983C4C System_Xml_ni!System.Xml.XmlLoader.Load(System.Xml.XmlDocument, System.Xml.XmlReader, Boolean)+0xac
000000001AB7E590 000007FEE5983730 System_Xml_ni!System.Xml.XmlDocument.Load(System.Xml.XmlReader)+0x90
...
000000001AB7F0A0 000007FEE97ED792 mscorlib_ni!System.Threading.Tasks.Task.Execute()+0x82
000000001AB7F100 000007FEE90A181C mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0xdc
000000001AB7F160 000007FEE97E7F95 mscorlib_ni!System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)+0x1b5
000000001AB7F1E0 000007FEE97E7D90 mscorlib_ni!System.Threading.Tasks.Task.ExecuteEntry(Boolean)+0xb0
000000001AB7F220 000007FEE90EBA83 mscorlib_ni!System.Threading.ThreadPoolWorkQueue.Dispatch()+0x193
000000001AB7F2C0 000007FEE90EB8D5 mscorlib_ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()+0x35
(TransitionUM)

EXCEPTION_OBJECT: !pe 2a61228
Exception object: 0000000002a61228
Exception type:   System.ExecutionEngineException
Message:          <none>
InnerException:   <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131506

MANAGED_OBJECT_NAME:  System.ExecutionEngineException

MANAGED_STACK_COMMAND:  _EFN_StackTrace

LAST_CONTROL_TRANSFER:  from 000007feea12bce4 to 000007feea129a1d

ADDITIONAL_DEBUG_TEXT:  Followup set based on attribute [Is_ChosenCrashFollowupThread] from Frame:[0] on thread:[PSEUDO_THREAD]

FAULTING_THREAD:  ffffffffffffffff

DEFAULT_BUCKET_ID:  INVALID_POINTER_READ_CALL

PRIMARY_PROBLEM_CLASS:  INVALID_POINTER_READ_CALL

BUGCHECK_STR:  APPLICATION_FAULT_INVALID_POINTER_READ_WRONG_SYMBOLS_CALL__SYSTEM.EXECUTIONENGINEEXCEPTION
_

UPDATED AGAIN

ページヒープ を有効にした後の例外のWinDbgスタックは次のとおりです。

_ (1480.e84): Access violation - code c0000005 (first chance)
ntdll!ZwTerminateProcess+0xa:
00000000`77c415da c3              ret
0:023> !clrstack
OS Thread Id: 0xe84 (23)
Child SP         IP               Call Site
0000000037ded848 0000000077c415da [HelperMethodFrame: 0000000037ded848]
0000000037dedab0 000007fee9effd17 System.Text.StringBuilder.ToString()*** WARNING: Unable to verify checksum for C:\Windows\Assembly\NativeImages_v4.0.30319_64\mscorlib\8f7f691aa155c11216387cf3420d9d1b\mscorlib.ni.dll

0000000037dedb00 000007ff00cceae9 Sgml.Entity.ScanToken(System.Text.StringBuilder, System.String, Boolean)

0000000037dedb70 000007ff00cd19b2 Sgml.SgmlDtd.ParseAttDefault(Char, Sgml.AttDef)
0000000037dedbc0 000007ff00cd120b Sgml.SgmlDtd.ParseAttDef(Char)
0000000037dedc00 000007ff00cd1057 Sgml.SgmlDtd.ParseAttList(System.Collections.Generic.Dictionary`2<System.String,Sgml.AttDef>, Char)
0000000037dedc50 000007ff00cd10cd Sgml.SgmlDtd.ParseAttList(System.Collections.Generic.Dictionary`2<System.String,Sgml.AttDef>, Char)
0000000037dedca0 000007ff00cd0e9a Sgml.SgmlDtd.ParseAttList()
0000000037dedd10 000007ff00cce1f1 Sgml.SgmlDtd.Parse()
0000000037dedd60 000007ff00ccd945 Sgml.SgmlDtd.Parse(System.Uri, System.String, System.IO.TextReader, System.String, System.String, System.Xml.XmlNameTable)
0000000037dede00 000007ff00ccd582 Sgml.SgmlReader.LazyLoadDtd(System.Uri)
0000000037dede70 000007ff00ccd1f9 Sgml.SgmlReader.OpenInput()
0000000037deded0 000007ff00cccd8c Sgml.SgmlReader.Read()
0000000037dedf20 000007fee67b3bfc System.Xml.XmlLoader.Load(System.Xml.XmlDocument, System.Xml.XmlReader, Boolean)*** WARNING: Unable to verify checksum for C:\Windows\Assembly\NativeImages_v4.0.30319_64\System.Xml\8e4323f5bfb90be4621456033d8b404b\System.Xml.ni.dll
*** ERROR: Module load completed but symbols could not be loaded for C:\Windows\Assembly\NativeImages_v4.0.30319_64\System.Xml\8e4323f5bfb90be4621456033d8b404b\System.Xml.ni.dll

0000000037dedf80 000007fee67b36e0 System.Xml.XmlDocument.Load(System.Xml.XmlReader)
[deleted]
0000000037deea90 000007feea61d432 System.Threading.Tasks.Task.Execute()
0000000037deeaf0 000007fee9ed17ec System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
0000000037deeb50 000007feea617c35 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)
0000000037deebd0 000007feea617a30 System.Threading.Tasks.Task.ExecuteEntry(Boolean)
0000000037deec10 000007fee9f1b953 System.Threading.ThreadPoolWorkQueue.Dispatch()
0000000037deecb0 000007fee9f1b7a5 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
0000000037def310 000007feeae4dc54 [DebuggerU2MCatchHandlerFrame: 0000000037def310]
0:023> !verifyheap
-verify will only produce output if there are errors in the heap
The garbage collector data structures are not in a valid state for traversal.
It is either in the "plan phase," where objects are being moved around, or
we are at the initialization or shutdown of the gc heap. Commands related to
displaying, finding or traversing objects as well as gc heap segments may not
work properly. !dumpheap and !verifyheap may incorrectly complain of heap
consistency errors.
object 000000000e34caf8: bad member 000000001024b9a0 at 000000000e34cb08
curr_object:      000000000e34caf8
Last good object: 000000000e34cab0
----------------
0:023> !analyze
Last event: 1480.e84: Exit process 0:1480, code 80131506
  debugger time: Sun Sep 18 14:22:42.592 2011 (UTC + 1:00)
0:023> !analyze -v
Last event: 1480.e84: Exit process 0:1480, code 80131506
  debugger time: Sun Sep 18 14:22:42.592 2011 (UTC + 1:00)
0:023> .do e34cab0
          ^ Syntax error in '.do e34cab0'
0:023> !do e34cab0
Name:        System.String
MethodTable: 000007feea026870
EEClass:     000007fee9baed58
Size:        72(0x48) bytes
File:        C:\Windows\Microsoft.Net\Assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      appliedFiltersContainer
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007feea02c758  4000103        8         System.Int32  1 instance               23 m_stringLength
000007feea02b298  4000104        c          System.Char  1 instance               61 m_firstChar
000007feea026870  4000105       10        System.String  0   shared           static Empty
                                 >> Domain:Value  00000000021343a0:000000000db21420 <<
0:023> !do e34caf8
<Note: this object has an invalid CLASS field>
Name:        System.Reflection.RuntimeAssembly
MethodTable: 000007feea02a128
EEClass:     000007fee9baf968
Size:        48(0x30) bytes
File:        C:\Windows\Microsoft.Net\Assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007feea9ef7f0  4000e14        8 ...solveEventHandler  0 instance 0000000000000000 _ModuleResolve
000007feea036338  4000e15       10 ...che.InternalCache  0 instance 000000001024b9a0 m_cachedData
000007feea0259c8  4000e16       18        System.Object  0 instance 000000000e3abd18 m_syncRoot
000007feea033450  4000e17       20        System.IntPtr  1 instance         37a95f10 m_Assembly
_

それは何でしょうか?

30
net_prog

最近、マネージヒープの破損に直面しましたが、これは私にとって新しいものでした。私はそれに非常に不満を抱いており、デバッグするためには多くのことを学ぶ必要がありました。最初に正しい方向性を教えてくれたSeva Titovに感謝します。彼の答えは簡潔でとても役に立ちました。問題をデバッグするために行ったアクションを自分の参照用にログに記録します。おそらくこれはこれに不慣れな他の人に役立つでしょう。

。NET 4でのヒープ破損のデバッグ:

ヒープの破損を疑う方法は?

簡単に:

  1. アプリケーションは、適用された例外キャッチに関係なくランダムにクラッシュし、すべての例外をキャッチすることになっているcatch(Exception)のようなブランケットを通過します。

  2. アプリケーションのクラッシュダンプでCLRスタックを調べると、スタックの最上部にガベージコレクターが表示されます。

    _000000001dabd8c8 000007feea129a1d [**HelperMethodFrame**: 000000001dabd8c8]
    000000001dabda00 000007fee90cfce8 System.Text.StringBuilder.ExpandByABlock(Int32)
    000000001dabda40 000007fee90cfba4 System.Text.StringBuilder.Append(Char*, Int32)
    ...
    
    EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)
    ExceptionAddress: 000007feea129a1d (**clr!WKS::gc_heap**::find_first_object+0x0000000000000092)
       ExceptionCode: c0000005 (Access violation)
      ExceptionFlags: 00000000
    NumberParameters: 2
       Parameter[0]: 0000000000000000
       Parameter[1]: 0000000000003d80
    ...
    _
  3. CLRスタックは常に異なるポイントを示します。例外が発生することが示されているStringBuilderのメソッドのように、クラッシュが発生したのか、表示されているコードが明らかに無関係であるのかは関係ありません。

詳細については、 。NETクラッシュ:アンマネージコードを呼び出すマネージヒープ破損

段階的に進みます。前のステップが役に立たない場合、次の各ステップが使用されます。

ステップ1.コードを確認します。

安全でないコードまたはネイティブコードの使用についてコードを確認します。

  1. unsafeDllImportステートメントのコードを確認します。
  2. ダウンロード 。NET Reflector を使用して、PInvokeのアプリケーションアセンブリを分析します。同様に、アプリケーションで使用されるサードパーティアセンブリを分析します。

安全でないコードやネイティブコードの使用が見つかった場合は、それらに特に注意してください。このような場合のヒープの破損の最も一般的な原因は、バッファオーバーフローまたは引数の型の不一致です。ネイティブコードに入力するバッファが十分な大きさであること、およびネイティブコードに渡されるすべての引数が期待されるタイプであることを確認してください。

ステップ2.この破損状態の例外をキャッチできるかどうかを確認します。

このような例外を処理するには、catch(Exception)ステートメントを含むメソッドを_[HandleProcessCorruptedStateExceptions]_属性で修飾するか、以下を_app.config_ファイルに適用する必要があります。

_<configuration>
    <runtime>
        <legacyCorruptedStateExceptionsPolicy enabled="true" />
    </runtime>
</configuration>
_

例外が正常にキャッチされた場合は、ログに記録して調べることができます。つまり、これは破損したヒープの問題ではありません。

破損したヒープ例外はまったく処理できません:HandleProcessCorruptedStateExceptionsが機能していないようです

破損した状態の例外の詳細については、。NET4の破損した状態の例外のすべてを参照してください。

ステップ3.ライブデバッグ

このステップでは、本番環境(またはクラッシュを再現できる場所)でライブクラッシュするアプリケーションをデバッグします。

ダウンロードDebugging Tools for WindowsfromMicrosoft Windows SDK for Windows 7 and .NET Framework 4(aインストールする必要なコンポーネントを選択できるWebインストーラーがダウンロードされます(すべてのコンポーネントをマークします)。必要なデバッグツールの32ビットと64ビット(システムがx64の場合)バージョンの両方をインストールします。

ここでは、WinDbgをライブプロセスにアタッチする方法、クラッシュダンプを取得して調べる方法、ロード方法 [〜#〜] sos [〜#〜] WinDbgの拡張機能(google for詳細)。

デバッグヘルパーを有効にします。

  1. Application Verifier(_C:\Program Files\Application Verifier_-実行可能なコンパイルモードに応じてx86またはx64のいずれか必要なエディションを使用)を起動し、実行可能ファイルを左側のペインに追加し、右側のペインで1つのノード「Basics/Heaps」を確認します。変更を保存します。

  2. グローバルフラグヘルパーを起動します(_C:\Program Files\Debugging Tools for Windows\gflags.exe_-正しいエディション、x86またはx64を再度選択します)。 Global Flagsが開始されたら、[イメージファイル]タブに移動し、上部のテキストボックスにパスを含まない実行可能ファイルの名前を入力します(たとえば、 "MyProgram.exe ")。次に、 Tab キーを押して、次のボックスを設定します。

    • ヒープテールチェックを有効にする
    • ヒープフリーチェックを有効にする
    • ヒープパラメータチェックを有効にする
    • 呼び出し時にヒープ検証を有効にする
    • ヒープの合体を無料で無効にする
    • ページヒープを有効にする
    • ヒープのタグ付けを有効にする
    • アプリケーション検証を有効にする
    • デバッガー(右側のテキストボックスに、インストールされているWinDbgへのパスを入力します。例:C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe -g)。

    詳細については、ヒープの破損、パート2を参照してください。

  3. 「コントロールパネル/システムとセキュリティ/システム」に移動します(または、スタートメニューの「コンピュータ」を右クリックして「プロパティ」を選択します。そこで「システムの詳細設定」をクリックし、表示されたダイアログの「詳細」タブに移動して、 「環境変数」ボタンをクリックします表示されたダイアログで、新しいシステム変数を追加します(システム管理者の場合-それ以外の場合はユーザー変数-この場合はログアウト/ログインする必要があります)必要な変数は「COMPLUS_HeapVerify」です値が "1"です。詳細については、スタックオーバーフローの質問。NET/C#:デバッグ環境変数COMPLUS_HeapVerify?を参照してください。

これでデバッグを開始する準備ができました。アプリケーションを起動します。 WinDbgが自動的に起動します。 WinDgbにクラッシュするまでアプリケーションを実行したままにして、ダンプを調べます。

[〜#〜] tip [〜#〜]Global FlagsApplication Verifierおよびデバッガーの添付設定、レジストリーで次のキーを削除します:x64-_HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\*YourAppName*_

ステップ4. MDAを有効にします。

マネージデバッグアシスタントを使用してみてください。詳細はスタックオーバーフローの質問にありますヒープの破損を追跡するのに役立つMDAは何ですか?

MDAはWinDbgと共に使用する必要があります。 Global FlagsおよびApplication Verifierと一緒に使用しました。

ステップ5. GCStressを有効にします。

アプリケーションがほとんど使用できなくなるため、GCStressを使用することは非常に重要なオプションですが、それでも問題ありません。詳細はGCStress:Windows 7でオンにする方法にあります。

ステップ6. x86用にコンパイルします。

アプリケーションが現在「任意のCPU」または「x64」プラットフォーム用にコンパイルされている場合、使用するプラットフォームに違いがない場合は、「x86」用にコンパイルしてみてください。これが誰かの問題を解決するために報告されているのを見ました。

ステップ7.コンカレントGCを無効にします-これは私のために機能しました

.NET 4で報告されている既知の問題がスレッドで報告されていますアンマネージモジュールのないgc_heap :: garbage_collectでの.NET 4ランタイムのアクセス違反。この問題は、_app.config_ファイルで並行GCを無効にすることで解決できます。

_<?xml version="1.0"?>
<configuration>
    <runtime>
        <gcConcurrent enabled="false" />
    </runtime>
</configuration>
_
55
net_prog

ヒープの破損を管理しました。管理されたヒープの破損の問題の根本的な原因を見つけるのは簡単ではありません。問題は通常、ヒープが破損した後も長く続くためです。あなたの場合、StringBuilderは赤いニシンです。汚職は以前に起こった。

私がすることは次のとおりです:

  1. 安全でないC#コードがないか確認してください。もしあれば、ロジックを再確認してください。
  2. アプリケーションで ページヒープ を有効にします。ページヒープで実行すると、アンマネージコードがマネージヒープを破損している場合に備えて、アンマネージコードの問題を明らかにするのに役立ちます。
  3. 別の場所で!VerifyHeapを実行します。このようにして、破損が発生するコード内の場所をローカライズできる場合があります。
  4. アプリケーションでサーバータイプのガベージコレクションを有効にしている場合は、一時的にそれをワークステーションのガベージコレクションに変更します。このようにすると、より予測可能な動作が得られます。
  5. Tessesのブログ投稿を読む。NETクラッシュ:アンマネージコードを呼び出すマネージヒープ破損。管理されたヒープの破損の例をいくつか示します。

WinDbgでコードを実行する場合は、偶発的な最初のチャンスAVに遭遇することに注意してください。 WinDbgをプロセスにアタッチしたら、sxd avと入力して、セカンドチャンスAVのみを調査することをお勧めします。

8
seva titov