顧客側での.NETアプリケーションのデバッグ、およびコードの最適化に関する一連の記事を始めたいと思います。 この点で、システムを少し準備する必要があります。 この記事では、アプリケーションをデバッグするためのさまざまなツールについて説明します。CLRの説明が必要な場合は、その詳細について説明します。
公益事業
これらすべてが個別の仮想マシンにインストールされ、メインOSを詰まらせないことが便利です。メインOSは.NETおよびエンターテイメントでのアプリケーション開発にのみ使用します。
.NETアプリケーションのデバッグ
アプリケーションを開発するとき、開発者は通常、完全に制御できるマシンで作業し、さまざまなユーティリティを使用できます。 デバッグ用に、コードをステップ実行し、変更してコンパイルする機能を備えた開発環境があります。 問題なく、いつでもアプリケーションを一時停止して、再起動できます。 ただし、顧客のサーバーでは、システムユーザーに影響を与えることなく、段階的なデバッグのためにアプリケーションを一時停止することはほとんど不可能です。 一般に、顧客のサーバーでアプリケーションをデバッグすると、通常は問題をできるだけ早く解決する必要があります。 シンプルなサービスでは、顧客はお金を失い、顧客は去り、単純な生産サイクルがあり、他の問題が発生する可能性があります。 問題が発生した場合は、一時的に解決するための対策を講じることを強くお勧めします。 これには、アプリケーションのリロード、機能の制限、および状況に対する他のソリューションが含まれる場合があります。
いくつかの問題が発生する可能性があります:アプリケーションのフリーズ、デッドロック、未処理または致命的な例外、データ損失、パフォーマンスの問題、リソースとメモリリーク、不正な機能。 これらの状況はすべて、特定のユーザーだけでなく、システムのすべてのユーザーに影響を与えます。
アプリケーションで問題を引き起こす主な理由は、環境の違い(ハードウェア、ソフトウェア、構成の違い)、および他のシステムとの相互作用です。
クライアント側でアプリケーションをデバッグするのがなぜ難しいのですか? 標準ツールを使用することはできません。サーバーにアクセスすることは困難または不可能であり、問題は非常にまれにしか発生しないか、顧客側にのみ発生します。
アプリケーションの種類に応じて、顧客にアプリケーションをインストールする前にこれらの手順を実行できます。
サーバーアプリケーション:
- お客様のサーバーにデバッグパッケージをインストールします。
- キャラクターサーバーをあなたの側にインストールしてください。
- ロギングおよび監視ツールを追加します。
クライアントアプリケーション:
- キャラクターサーバーをあなたの側にインストールしてください。
- ロギングおよび監視ツールを追加します。
リスクを最小限に抑えるために、顧客に表示される前にエラーを見つけて修正し、さまざまな状況を解決し、Visual Studioの標準とサードパーティの
NUnit NCoverの両方で自動テストを行うことをお
勧めします。
問題が発生した場合、できるだけ多くの情報を収集することが重要です。これは、エラーの調査と修正に役立ちます。
管理アセンブリアセンブリ構造
あまり詳細には触れません。 それらはさまざまなバージョンで簡単にアクセスできます。さらに使用するものについてのみ説明し、追加の情報源に注意します。
.NET実行可能ファイルは、すべてのWindowsアプリケーションに標準の通常のMS-DOSおよびCOFFヘッダーで始まります。
最初のデータセクションには、CLRヘッダーとデータが含まれます。
最初のセクションには属性CNT_CODE、MEM_EXECUTE、MEM_READがあり、ランタイムに実行されるコードがセクションに含まれていることをローダーに示すことに注意してください。 インポートテーブルでは、アプリケーションを実行している場合の呼び出し「_CorExeMain」を確認できます。 ランタイム環境のメインモジュールのmscoree.dllに実装されています。
詳細については、「
アンマネージAPIリファレンス 」を参照してください。
これらのセグメントの後には、アプリケーション自体のILコードが続きます。 コンパイル結果は中間言語コード(MSIL)です。 これは、プロセッサに依存しない言語です。ネイティブコードよりも高いレベルの言語です。 仕様は
こちらで確認できます。
ILコードの後には、メタデータヘッダー、メタデータ自体、アプリケーションリソースを含む追加セグメント、ネイティブアプリケーションコードが続きます(アプリケーションがマネージC ++で開発された場合)。
実行可能ファイルの構造に関する情報は、標準の
ildasm.exeユーティリティを使用して取得できます。
WinDbg + SOS、adplus.vbsの探索
この段階で、必要なすべてのアプリケーションを既にインストールして構成していることを願っています(Debug Tools ToolsディレクトリーもPATH環境変数に追加してください)。 このテストプログラムについてさらに調査します。
いくつかのナンセンスを行う独自のアプリケーションを作成することができます(必要です)。 このプログラムで重要なのは(o__O、私はそのような考えに傾いていました)、そして重要なのは、ヒープにいくつかのオブジェクトを作成し、いくつかのメソッドを呼び出し、最終的に誤って例外をスローすることです。 文字を使用してアプリケーションのデバッグバージョンを作成しましょう。
アプリケーションを起動します。 そして、アプリケーションのあるディレクトリにこのコマンドを入力します
adplus -quiet -crash -fullonfirst -pn ApplicationForWinDbg.exeApplicationForWinDbg.exeはコンパイル済みのexeです。
コンソールアプリケーションで、任意のキーを押します。 すべてを正しく行った場合、
Crash_Mode__Date_ [DATE] __ Time_ [TIME]フォルダーがデバッグツールがインストールされたディレクトリに表示されます。
[日付]と
[時間]は、ダンプが作成された日付と時刻です。
ダンプを作成し、それを調べ、同時にCLRの内部を理解します。 この記事では詳細な説明は行いません。主なことは、WinDbg + SOSの使用方法を学ぶことです。 詳細は他の記事で。
WinDbgでダンプを開きます。 次のようなものが得られるはずです。
次のコマンドを実行して、WinDbg-SOS拡張機能をダウンロードします。
0:000>
.loadby sos mscorwksダウンロードの進行状況を確認するには、次のコマンドを実行します。
0:000>
!Eeversion 2.0.50727.3053小売
ワークステーションモード
SOSバージョン:2.0.50727.3053小売ビルド
拡張機能がロードされていることを確認し、
ctrl + sを押し、WinDbgが文字をロードするようにアプリケーションへのパスを追加します。 これで調査を開始できます。 アプリケーションにあるスレッドを見てみましょう。
0:000>
〜* 。 0 Id:e8c.11bc一時停止:1 Teb:7ffde000 Unfrozen
開始:ApplicationForWinDbg!COM + _Entry_Point <PERF>(ApplicationForWinDbg + 0x284e)(0011284e)
優先度:0優先度クラス:32アフィニティ:3
1 Id:e8c.13f0一時停止:1 Teb:7ffdd000 Unfrozen
開始:mscorwks!DebuggerRCThread :: ThreadProcStatic(7243237f)
優先度:0優先度クラス:32アフィニティ:3
2 Id:e8c.130c一時停止:1 Teb:7ffdc000 Unfrozen
開始:mscorwks!スレッド:: IntermediateThreadProc(724c1fcf)
優先度:2優先度クラス:32アフィニティ:3
マネージスレッドを見てみましょう。
0:000>
!スレッド スレッドカウント:2
UnstartedThread:0
BackgroundThread:1
PendingThread:0
デッドスレッド:0
ホストされたランタイム:いいえ
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ状態GCコンテキストドメインカウントAPT例外
0 1 11bc 00529188 a020無効01c903cc:01c91fe8 00524c40 0 MTA
2 2 130c 00537020 b220有効00000000:00000000 00524c40 0 MTA(ファイナライザー)
これから、アプリケーションに2つのマネージスレッド(アプリケーションが実行されているメインのマネージスレッドと
Finalizerスレッド)があることがわかります。 そして、もう1つのアンマネージ
mscorwksスレッドがあります
!DebuggerRCThread :: ThreadProcStaticは
デバッガースレッドです。 通信仕様は閉じられ、デバッグ情報、段階的なデバッグ機能などが提供されます。このストリームは、アプリケーションのリリースバージョンを作成するかデバッグバージョンを作成するかに関係なく存在します。
アプリケーションのAppDomainsを見てみましょう。
0:000>
!ダンプドメイン --------------------------------------システムドメイン:728ed058 LowFrequencyHeap:728ed07c HighFrequencyHeap:728ed0c8 StubHeap: 728ed114ステージ:オープン名前:なし--------------------------------------共有ドメイン:728ec9a8 LowFrequencyHeap :728ec9cc HighFrequencyHeap:728eca18 StubHeap:728eca64ステージ:OPEN名前:なしアセンブリ:0051c920 --------------------------------- -----ドメイン1:00524c40 LowFrequencyHeap:00524c64 HighFrequencyHeap:00524cb0 StubHeap:00524cfcステージ:OPEN SecurityDescriptor:00526198名前:ApplicationForWinDbg.exeアセンブリ:0051c920 [C:\ WINDOWS \アセンブリ\ GAC_32 \ mscorlib \ 2.0.0.0__b77a5c561934e089 \ mscorlib .dll] ClassLoader:0051c990 SecurityDescriptor:005384a8 Module Name 70da1000 C:\ Windows \ assembly \ GAC_32 \ mscorlib \ 2.0.0.0__b77a5c561934e089 \ mscorlib.dll Assembly:0051ca70 [D:\#Projects \ #Active \ TestApplicationbg \ Application Debug \ ApplicationForWinDbg.exe] ClassLoader:0051cae0 SecurityDescriptor:0053d088モジュール名00202c5c D:\#Projects \ #Active \ Test ApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe
コードが実行されるドメインに加えて、
システムドメイン 、
共有ドメインもあります。 1つは、
共有ドメインのロードと、すべてのユーザードメインのロードとアンロードを担当します。 2番目は、ドメイン中立アセンブラアセンブリのリポジトリとして機能します。 コードはユーザードメインでのみ実行できるため、コードは
共有ドメインでは実行されません。
ユーザードメインでは、モジュール-実行可能ファイルdll、exe、その他を確認します。 アセンブラアセンブリには複数のモジュールが含まれている場合がありますが、これらは実行可能ファイルだけでなく、リソースファイルでもあります。 前の例からアセンブリアセンブリcaca70のアドレスを取得し、追加情報を取得します。
0:000>
!ダンプアセンブリ0x0051ca70 親ドメイン:00524c40
名前:D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe
クラスローダー:0051cae0
SecurityDescriptor:002bed20
モジュール名
00202c5c D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe
モジュール00202c5cに関する情報を見てみましょう。
0:000>
!Dumpmodule 0x00202c5c 名前:D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe
属性:PEFile
アセンブリ:0051ca70
LoaderHeap:00000000
TypeDefToMethodTableMap:00200038
TypeRefToMethodTableMap:00200048
MethodDefToDescMap:002000a0
FieldDefToDescMap:002000b8
MemberRefToDescMap:002000bc
ファイル参照マップ:00200118
AssemblyReferencesMap:0020011c
メタデータの開始アドレス:001120e4(1668バイト)
–mtパラメーターを使用すると、モジュールの型に関する情報も取得できます。
0:000>
!Dumpmodule -mt 0x00202c5c 名前:D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe
属性:PEFile
アセンブリ:thodTableMap:00200038
TypeRefToMethodTableMap:00200048
MethodDefToDescMap:002000a0
FieldDefToDescMap:002000b8
MemberRefToDescMap:002000bc
ファイル参照マップ:00200118
AssemblyReferencesMap:0020011c
メタデータの開始アドレス:001120e4(1668バイト)
このモジュールで定義されているタイプ
MT TypeDef名
-------------------------------------------------- ----------------------------
00203080 0x02000002 ApplicationForWinDbg.Class1
0020300c 0x02000003 ApplicationForWinDbg.Program
このモジュールで参照されるタイプ
MT TypeRef名
-------------------------------------------------- ----------------------------
71010508 0x01000001 System.Object
710108ec 0x01000012 System.String
71014258 0x01000013 System.Console
71012b38 0x01000015 System.Int32
0051ca70
LoaderHeap:00000000
TypeDefToMe
コードで定義されているタイプが
ApplicationForWinDbg.Class1および
ApplicationForWinDbg.Programであることを発見しました。 これらのクラスに関する情報は、
EEClassの内部構造に格納されます。 この構造には、インターフェイス、メソッド、フィールドの数、およびそれらの構造などに関する情報が含まれています。
ヒップで何を持っているか見てみましょう:
0:000>
!Dumpheap アドレスMTサイズ
01c71000 0052ccc8 12無料
01c7100c 0052ccc8 12無料
01c71018 0052ccc8 12無料
01c71024 71010b10 72
01c7106c 71010ba0 72
01c710b4 71010c30 72
01c710fc 71010cc0 72
01c71144 71010cc0 72
01c7118c 71010508 12
01c71198 710108ec 20
01c711ac 71010fb8 28
01c711c8 710108ec 160
01c71268 710108ec 216
01c71340 710110cc 100
01c713a4 710113d8 44
01c713d0 70fe40bc 80
01c71420 710108ec 28
01c7143c 710108ec 32
01c7145c 710108ec 20
01c71470 710108ec 52
01c714a4 710108ec 40
……………………………………………………
01c90398 7101151c 24
01c903b0 710108ec 28
02c71000 0052ccc8 16無料
02c71010 70fe40bc 4096
02c72010 0052ccc8 16無料
02c72020 70fe40bc 528
02c72230 0052ccc8 16無料
02c72240 70fe40bc 4096
02c73240 0052ccc8 16無料
合計5361オブジェクト
統計:
MTカウントTotalSizeクラス名
71013dc0 1 12 System.Text.DecoderExceptionFallback
71013d7c 1 12 System.Text.EncoderExceptionFallback
71013ae4 1 16 System.Text.DecoderReplacementFallback
71013a94 1 16 System.Text.EncoderReplacementFallback
71014808 1 20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle
710147b0 1 20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
71014724 1 20 System.Text.InternalEncoderBestFitFallback
71012e50 1 20 System.Security.PermissionToken
7100e8b8 1 20 Microsoft.Win32.SafeHandles.SafeFileHandle
71014770 1 24 System.Text.InternalDecoderBestFitFallback
710143c0 1 24 System.IO.TextWriter + SyncTextWriter
710127cc 2 24 System.Security.Permissions.SecurityPermission
71012510 1 24 System.OperatingSystem
71014290 1 28 Microsoft.Win32.Win32Native + InputRecord
71013f24 1 28 System.Text.EncoderNLS
71013c34 1 28 System.IO.Stream + NullStream
71013990 1 28 System.Text.UTF8Encoding
71010fb8 1 28 System.SharedStatics
71013ea4 1 32 System.Text.UTF8Encoding + UTF8Encoder
71012efc 1 32 System.Security.PermissionTokenFactory
710122cc 1 32 Microsoft.Win32.Win32Native + OSVERSIONINFO
710142e4 1 36 System.IO .__ ConsoleStream
71012eb0 1 36 System.Security.Util.TokenBasedSet
71012d18 1 36 System.Security.Permissions.FileIOPermission
710120c8 1 36 System.Int64 []
710123d4 1 40 Microsoft.Win32.Win32Native + OSVERSIONINFOEX
7101278c 1 44 System.Security.FrameSecurityDescriptor
710113d8 1 44 System.AppDomainSetup
71012874 3 48 System.Security.Permissions.FileIOAccess
71012494 2 48 System.Version
71011e38 2 48 System.Reflection.Assembly
7100e6f4 4 48 System.UInt16
71012c14 2 56 System.Collections.ArrayList + ArrayListEnumeratorSimple
71010ec0 1 56 System.Threading.Thread
71013714 1 68 System.Globalization.CultureTable
710128bc 3 72 System.Security.Util.StringExpressionSet
710125b0 2 72 System.Security.PermissionSet
71010c30 1 72 System.ExecutionEngineException
71010ba0 1 72 System.StackOverflowException
71010b10 1 72 System.OutOfMemoryException
71014608 1 76 System.Text.SBCSCodePageEncoding
710137a8 5 100 System.Globalization.CultureTableItem
710110cc 1 100 System.AppDomain
0052ccc8 7100無料
71012b38 9108 System.Int32
710140d4 2 112 System.IO.StreamWriter
71010a28 6120 System.Text.StringBuilder
71010508 10120 System.Object
710136c4 3 144 System.Globalization.CultureTableRecord
7101291c 6 144 System.Collections.ArrayList
71010cc0 2 144 System.Threading.ThreadAbortException
71011a6c 8160 System.RuntimeType
71011fe4 3 192 System.IO.UnmanagedMemoryStream
710134e8 3204 System.Globalization.CultureInfo
71013850 2 256 System.Globalization.NumberFormatInfo
71012f40 7 392 System.Collections.Hashtable
7101335c 3 684 System.Byte []
7101303c 7 1008 System.Collections.Hashtable + bucket []
71012a88 14 1536 System.Int32 []
70fe40bc 28 9564 System.Object []
00203 080 1000 12000 ApplicationForWinDbg.Class1
7101151c 1022 25376 System.Char []
710108ec 3160 82600 System.String
合計5361オブジェクト
答えてみてください、今何行が腰にありますか?
メタデータへのポインターを使用して、ApplicationForWinDbg.Class1クラスに関する情報を取得します。
0:000>
!Dumpmt 0x00203080 EEClass:0020135c
モジュール:00202c5c
名前:ApplicationForWinDbg.Class1
mdToken:02000002(D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe)
BaseSize:0xc
ComponentSize:0x0
IFaceMapのIFacesの数:0
VTableのスロット:7
これで、その
EEClassへのポインターにアクセスでき、その説明が表示されます:
0:000>
!Dumpclass 0x0020135c クラス名:ApplicationForWinDbg.Class1
mdToken:02000002(D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe)
親クラス:70da3ef0
モジュール:00202c5c
方法表:00203080
Vtableスロット:4
総メソッドスロット:5
クラス属性:100000
NumInstanceFields:0
NumStaticFields:0
メソッドテーブル00203080へのポインタによって、クラスのすべてのメソッドのリストを見てみましょう。
0:000>
!Dumpmt -md 0x00203080 EEClass:0020135c
モジュール:00202c5c
名前:ApplicationForWinDbg.Class1
mdToken:02000002(D:\#Projects \ #Active \ TestApplicationNet \ ApplicationForWinDbg \ bin \ Debug \ ApplicationForWinDbg.exe)
BaseSize:0xc
ComponentSize:0x0
IFaceMapのIFacesの数:0
VTableのスロット:7
--------------------------------------
MethodDescテーブル
Entry MethodDesc JIT名
70f66a70 70de4934 PreJIT System.Object.ToString()
70f66a90 70de493c PreJIT System.Object.Equals(System.Object)
70f66b00 70de496c PreJIT System.Object.GetHashCode()
70fd72f0 70de4990 PreJIT System.Object.Finalize()
004b0150 00203078 JIT ApplicationForWinDbg.Class1..ctor()
004b0188 00203060 JIT ApplicationForWinDbg.Class1.Method1(System.String)
004b01d8 0020306c JIT ApplicationForWinDbg.Class1.Method2(Int32)
また、例として、
ApplicationForWinDbg.Class1.Method1とそのコンパイル済みコードに関する情報を見てみましょう。
0:000>
!Dumpmd 0x00203060 メソッド名:ApplicationForWinDbg.Class1.Method1(System.String)
クラス:0020135c
MethodTable:00203080
mdToken:06000001
モジュール:00202c5c
IsJitted:はい
CodeAddr:004b0188
0:000>
!U 0x004b0188 通常のJIT生成コード
ApplicationForWinDbg.Class1.Method1(System.String)
004b0188の開始、サイズ3a
>>> 004b0188 55 push ebp
004b0189 8bec mov ebp、esp
004b018b 83ec0c sub esp、0Ch
004b018e 894dfc mov dword ptr [ebp-4]、ecx
004b0191 8955f8 mov dword ptr [ebp-8]、edx
004b0194 833d142e200000 cmp dword ptr ds:[202E14h]、0
004b019b 7405 je 004b01a2
004b019d e8dfa21472 mscorwksを呼び出す!JIT_DbgIsJustMyCode(725fa481)
004b01a2 90 nop
004b01a3 8b153020c702 mov edx、dword ptr ds:[2C72030h]( "$")
004b01a9 8b4df8 mov ecx、dword ptr [ebp-8]
004b01ac e86feaaa70 call mscorlib_ni + 0x1bec20(70f5ec20)(System.String.Concat(System.String、System.String)、mdToken:060001c9)
004b01b1 8945f4 mov dword ptr [ebp-0Ch]、eax
004b01b4 8b4df4 mov ecx、dword ptr [ebp-0Ch]
004b01b7 e81c36fc70 call mscorlib_ni + 0x6d37d8(714737d8)(System.Console.WriteLine(System.String)、mdToken:060007c8)
004b01bc 90 nop
004b01bd 90 nop
004b01be 8be5 mov esp、ebp
004b01c0 5dポップebp
004b01c1 c3 ret
すべてのリスティングのために記事が長くなることが判明したため、おそらくここで終了します。 継続を待ちます。 そして、すべてのフィールドの目的のより詳細な説明と、次の記事では、他のコマンドについて説明します。