Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
こんにちは。プラットフォーム サポートの大貫です。
今回は、Pool が枯渇する現象のトラブル シューティングについてお話したいと思います。
Pool の説明やトラブル シューティングなどについては、以下のブログでも紹介されています。
この内容を踏まえて、Pool が枯渇している環境をデバックしてみます。調査する環境は、2 つあります。環境は Windows 7 です。
まずは、ひとつ目の環境から。
- 環境 1 – Paged Pool 枯渇
まずは、!vm コマンドを実行します。この結果から Paged Pool の枯渇が確認でき、かなりの回数の Pool の確保が失敗しています。
1: kd> !vm 5
*** Virtual Memory Usage ***
Physical Memory: 524174 ( 2096696 Kb)
Page File: \??\C:\pagefile.sys
Current: 2096696 Kb Free Space: 2096692 Kb
Minimum: 2096696 Kb Maximum: 6290088 Kb
Available Pages: 425421 ( 1701684 Kb)
ResAvail Pages: 484901 ( 1939604 Kb)
Locked IO Pages: 0 ( 0 Kb)
Free System PTEs: 2680 ( 10720 Kb)
Modified Pages: 4455 ( 17820 Kb)
Modified PF Pages: 4401 ( 17604 Kb)
NonPagedPool Usage: 11089 ( 44356 Kb)
NonPagedPool Max: 385022 ( 1540088 Kb)
PagedPool 0 Usage: 425939 ( 1703756 Kb)
PagedPool 1 Usage: 2408 ( 9632 Kb)
PagedPool 2 Usage: 59 ( 236 Kb)
PagedPool 3 Usage: 45 ( 180 Kb)
PagedPool 4 Usage: 93 ( 372 Kb)
PagedPool Usage: 428544 ( 1714176 Kb) <<< 大きな値を示しています。
PagedPool Maximum: 523264 ( 2093056 Kb)
********** 34553 pool allocations have failed ********** <<< 34553 回 Pool の確保に失敗しています。
Shared Commit: 6605 ( 26420 Kb)
Special Pool: 0 ( 0 Kb)
Shared Process: 1692 ( 6768 Kb)
PagedPool Commit: 428544 ( 1714176 Kb)
Driver Commit: 1857 ( 7428 Kb)
Committed pages: 516797 ( 2067188 Kb)
Commit limit: 1048348 ( 4193392 Kb)
Terminal Server Memory Usage By Session:
Session ID 0 @ 8fe87000:
Paged Pool Usage: 2740K
Commit Usage: 5296K
Session ID 1 @ 8fe9a000:
Paged Pool Usage: 13908K
Commit Usage: 16492K
では、なぜ Pool が枯渇しているのでしょうか?
どの Tag で大量に Paged Pool が確保されているかを確認すると、Tag "MSKK" で Paged Pool が確保されていることが確認できます。
1: kd> !poolused 4
Sorting by Paged Pool Consumed
Pool Used:
NonPaged Paged
Tag Allocs Used Allocs Used
MSKK 0 0 415447 1701670912 UNKNOWN pooltag 'MSKK', please update pooltag.txt
CM31 0 0 5555 24899584 Internal Configuration manager allocations , Binary: nt!cm
CM25 0 0 2026 8814592 Internal Configuration manager allocations , Binary: nt!cm
CIcr 0 0 1295 2990224 Code Integrity allocations for image integrity checking , Binary: ci.dll
MmRe 0 0 401 2765280 ASLR relocation blocks , Binary: nt!mm
ClfI 0 0 19 1782880 CLFS Log marshal buffer lookaside list , Binary: clfs.sys
MmSt 0 0 664 1268752 Mm section object prototype ptes , Binary: nt!mm
VmbK 51 403064 8 876544 Volume Manager Extension , Binary: volmgrx.sys
次に、この Tag がどのドライバで確保されているのかを確認します。
確認するには、”!for_each_module” コマンドを使い、各モジュールのアドレス領域から "MSKK" タグを検索します。
1: kd> !for_each_module s -a @#Base @#End "MSKK"
83184218 4d 53 4b 4b 67 00 46 00-69 00 6c 00 65 00 49 00 MSKKg.F.i.l.e.I.
9196b324 4d 53 4b 4b 68 00 10 00-00 6a 01 ff 15 10 90 96 MSKKh....j......
2 箇所で MSKK という文字列が確認できました。
では、本当に上記箇所で Tag "MSKK" を使用して、Pool を確保しているのかを確認しましょう。
最初のアドレスを見ると、以下のような結果になり Pool を確保している形跡は見当たりません。
1: kd> u 83184218-1
nt!KdpBreakpointTable+0x1ff:
83184217 004d53 add byte ptr [ebp+53h],cl
8318421a 4b dec ebx
8318421b 4b dec ebx
8318421c 67004600 add byte ptr [bp+0],al
83184220 69006c006500 imul eax,dword ptr [eax],65006Ch
83184226 49 dec ecx
83184227 006e00 add byte ptr [esi],ch
8318422a 66006f00 add byte ptr [edi],ch
では、次のアドレスを見てみます。すると、引数を 3 つスタックに積み、関数を呼び出していることが確認できます。
1: kd> u 9196b324-1
SIoctl+0x4323:
9196b323 684d534b4b push 4B4B534Dh
9196b328 6800100000 push 1000h
9196b32d 6a01 push 1
9196b32f ff1510909691 call dword ptr [SIoctl+0x2010 (91969010)]
9196b335 4e dec esi
9196b336 75eb jne SIoctl+0x4323 (9196b323)
9196b338 cc int 3
9196b339 eb07 jmp SIoctl+0x4342 (9196b342)
呼び出している関数を確認してみます。すると、呼び出しているのは、ExAllocatePoolWithTag 関数であることが確認できます。
1: kd> ln poi(91969010)
(82f37bab) nt!ExAllocatePoolWithTag | (82f38660) nt!ExDeferredFreePool
Exact matches:
nt!ExAllocatePoolWithTag (_POOL_TYPE, unsigned long, unsigned long)
ExAllocatePoolWithTag 関数は、以下のように定義されています。
PVOID ExAllocatePoolWithTag(
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag
);
そうすると、引数は以下のように渡されていることがわかります。
第一引数 1
第二引数 0x1000
第三引数 4B4B534Dh
DDK のドキュメントを確認すると、POOL_TYPE は以下のように定義されていますので、第一引数では、Paged Pool が指定されていることが確認できます。
typedef enum _POOL_TYPE {
NonPagedPool,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
} POOL_TYPE;
第二引数では、サイズとして 0x1000 バイト確保するように引数が渡されています。
第三引数は Tag です。では、Tag を確認します。すると、"MSKK" という文字列であることが確認できます。
1: kd> .formats 4B4B534Dh
Evaluate expression:
Hex: 4b4b534d
Decimal: 1263227725
Octal: 11322651515
Binary: 01001011 01001011 01010011 01001101
Chars: KKSM
Time: Tue Jan 12 01:35:25 2010
Float: low 1.33251e+007 high 0
Double: 6.24117e-315
では、この処理をしているモジュールを確認します。アドレス 9196b324 が含まれることが分かっているので、この領域を含むドライバを出力します。
1: kd> lmva 9196b324
start end module name
91967000 9196f000 SIoctl (no symbols)
Loaded symbol image file: SIoctl.sys
Image path: SIoctl.sys
Image name: SIoctl.sys
Timestamp: Mon Apr 12 18:37:08 2010 (4BC2E9C4)
CheckSum: 000106CF
ImageSize: 00008000
Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4
この結果から、SIoctl.sys が Paged Pool を確保していることが確認できます。この場合は、このモジュールの製造元に確認していただく必要があり、なぜ、こんなに確保するのかなどを確認するのか、問題があれば修正することで問題は解決です。
次に、2 つめの環境を Debug してみます。
- 環境 2 – NonPaged Pool 枯渇
まずは、何はともあれ、!vm コマンドを実行します。この結果からみると、NonPaged Pool が大きく消費されているように見えます。
1: kd> !vm 5
*** Virtual Memory Usage ***
Physical Memory: 524174 ( 2096696 Kb)
Page File: \??\C:\pagefile.sys
Current: 2096696 Kb Free Space: 1996300 Kb
Minimum: 2096696 Kb Maximum: 6290088 Kb
Available Pages: 71341 ( 285364 Kb)
ResAvail Pages: 124674 ( 498696 Kb)
Locked IO Pages: 0 ( 0 Kb)
Free System PTEs: 23357 ( 93428 Kb)
Modified Pages: 1919 ( 7676 Kb)
Modified PF Pages: 1831 ( 7324 Kb)
NonPagedPool Usage: 384704 ( 1538816 Kb) <<<< 非常に大きくなっている
NonPagedPool Max: 385022 ( 1540088 Kb)
********** Excessive NonPaged Pool Usage *****
PagedPool 0 Usage: 37001 ( 148004 Kb)
PagedPool 1 Usage: 2297 ( 9188 Kb)
PagedPool 2 Usage: 291 ( 1164 Kb)
PagedPool 3 Usage: 231 ( 924 Kb)
PagedPool 4 Usage: 264 ( 1056 Kb)
PagedPool Usage: 40084 ( 160336 Kb)
PagedPool Maximum: 523264 ( 2093056 Kb)
********** 196 pool allocations have failed ********** <<<< Pool の確保も失敗している
Shared Commit: 6286 ( 25144 Kb)
Special Pool: 0 ( 0 Kb)
Shared Process: 1536 ( 6144 Kb)
PagedPool Commit: 40100 ( 160400 Kb)
Driver Commit: 1813 ( 7252 Kb)
Committed pages: 740898 ( 2963592 Kb)
Commit limit: 1048348 ( 4193392 Kb)
Terminal Server Memory Usage By Session:
Session ID 0 @ 9008b000:
Paged Pool Usage: 3620K
Commit Usage: 6176K
Session ID 1 @ 90092000:
Paged Pool Usage: 15680K
Commit Usage: 18272K
では、どの Tag で消費しているのか確認します。NonPaged Pool が枯渇しているので、NonPaged Pool でソートします。この結果から、Tag "Thre" で NonPaged Pool が大量に確保されていることが確認できます。
1: kd> !poolused 2
Sorting by NonPaged Pool Consumed
Pool Used:
NonPaged Paged
Tag Allocs Used Allocs Used
Thre 1908328 1419794592 0 0 Thread objects , Binary: nt!ps
nVsC 519 1474176 0 0 UNKNOWN pooltag 'nVsC', please update pooltag.txt
Pool 5 1156680 0 0 Pool tables, etc.
Mm 9 595712 3 88 general Mm Allocations , Binary: nt!mm
EtwB 71 577960 6 135200 Etw Buffer , Binary: nt!etw
netv 2065 323664 1 128 UNKNOWN pooltag 'netv', please update pooltag.txt
:
:
では、なぜ、こんなに大量に Tag "Thre" で NonPaged Pool が確保されるのでしょうか?
まず、Tag "Thre" は何をしめすものなのでしょうか? !Poolused コマンドの結果にありますように、Tag "Thre" は Thread Object を示します。ということは、大量に Thread が作られているということになります。
次に、Tag "Thre" で確保された Pool のサンプルを取得してみます。
1: kd> !poolfind Thre 0
Scanning large pool allocation table for Tag: Thre
Scanning large pool allocation table for Tag: Thre (8a957000 : 8aa57000)
*89bc1b48 size: 2d8 previous size: 68 (Allocated) Thre (Protected)
*8a140a60 size: 2e8 previous size: 180 (Allocated) Thre (Protected)
*8a625180 size: 2e8 previous size: 60 (Allocated) Thre (Protected)
*9dd7c178 size: 2e8 previous size: a8 (Allocated) Thre (Protected)
*9dd7c460 size: 2e8 previous size: 2e8 (Allocated) Thre (Protected)
:
:
*82945000 size: 2e8 previous size: 0 (Allocated) Thre (Protected)
*82945460 size: 2e8 previous size: 178 (Allocated) Thre (Protected)
*82945748 size: 2e8 previous size: 2e8 (Allocated) Thre (Protected)
*82945a30 size: 2e8 previous size: 2e8 (Allocated) Thre (Protected)
*82945d18 size: 2e8 previous size: 2e8 (Allocated) Thre (Protected)
*82946000 size: 2e8 previous size: 0 (Allocated) Thre (Protected)
:
:
KTHREAD 構造体は、"Thre" タグで確保された Pool 内に存在します。なので、適当な Thread の KTHREAD 構造体がどのプールにあるかを確認することで、プールの先頭からのオフセットを計算することができます。例として、以下の Thread を確認します。
1: kd> !THREAD 8ab94030
THREAD 8ab94030 Cid 0564.0568 Teb: 7ffde000 Win32Thread: fe9ab4e8 WAIT: (WrUserRequest) UserMode Non-Alertable
8ab8a458 SynchronizationEvent
Not impersonating
DeviceMap 8f072fc8
Owning Process 8ab897f0 Image: explorer.exe
Attached Process N/A Image: N/A
Wait Start TickCount 90686 Ticks: 354 (0:00:00:05.531)
Context Switch Count 12622
UserTime 00:00:00.484
KernelTime 00:00:01.093
Win32 Start Address 0x00c2a8df
Stack Init 94393fd0 Current 94393ba0 Base 94394000 Limit 94391000 Call 0
Priority 10 BasePriority 8 UnusualBoost 0 ForegroundBoost 2 IoPriority 2 PagePriority 5
ChildEBP RetAddr Args to Child
94393bb8 82ebfb15 8ab94030 82f7df08 82f7ad20 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
94393bf0 82ebe403 8ab940f0 8ab94030 8ab8a458 nt!KiSwapThread+0x266 (CONV: fastcall)
94393c18 82eb82cf 8ab94030 8ab940f0 00000000 nt!KiCommitThreadWait+0x1df (CONV: stdcall)
94393c90 97870d75 8ab8a458 0000000d 00000001 nt!KeWaitForSingleObject+0x393 (CONV: stdcall)
94393cec 97870e10 000024ff 00000000 00000001 win32k!xxxRealSleepThread+0x1d7 (FPO: [Non-Fpo]) (CONV: stdcall)
94393d08 9782bd56 000024ff 00000000 00000001 win32k!xxxSleepThread+0x2d (FPO: [Non-Fpo]) (CONV: stdcall)
94393d1c 97861eba 000024ff 00000000 0006fd30 win32k!xxxRealWaitMessageEx+0x12 (FPO: [Non-Fpo]) (CONV: stdcall)
94393d2c 82e9442a 0006fd44 772d64f4 badb0d00 win32k!NtUserWaitMessage+0x14 (FPO: [0,0,4]) (CONV: stdcall)
94393d2c 772d64f4 0006fd44 772d64f4 badb0d00 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 94393d34)
0006fd44 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
!Pool コマンドで、プールのアドレスを計算します。
1: kd> !pool 8ab94030 2
Pool page 8ab94030 region is Nonpaged pool
*8ab94000 size: 2e8 previous size: 0 (Allocated) *Thre (Protected)
Pooltag Thre : Thread objects, Binary : nt!ps
上記の結果を用いることで、KTHREAD 構造体のオフセットが確認できます。Pool の先頭のアドレスが 8ab94000、KTHREAD 構造体のアドレスが 8ab94030 です。なので、以下の計算でオフセットが計算できます。
1: kd> ? 8ab94030 - 8ab94000
Evaluate expression: 48 = 00000030
KTHREAD 構造体は、プールの先頭から +0x30 バイトに始まります。
これを踏まえ、Pool のサンプルからいくつかの KTHREAD 構造体を確認すると以下のような結果がでます。
1: kd> !thread 82943d18+30
THREAD 82943d48 Cid 12ce80.60ae30 Teb: 00000000 Win32Thread: 00000000 TERMINATED
Not impersonating
DeviceMap 9805fec8
Owning Process 8947ea58 Image: TestApp.exe
Attached Process N/A Image: N/A
Wait Start TickCount 61686 Ticks: 29354 (0:00:07:38.656)
Context Switch Count 5
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address 0x00401000
Stack Init 0 Current 9b6cbbf8 Base 9b6cc000 Limit 9b6c9000 Call 0
Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
1: kd> !thread 82945460+30
THREAD 82945490 Cid 12ce80.60ae64 Teb: 00000000 Win32Thread: 00000000 TERMINATED
Not impersonating
DeviceMap 9805fec8
Owning Process 8947ea58 Image: TestApp.exe
Attached Process N/A Image: N/A
Wait Start TickCount 61686 Ticks: 29354 (0:00:07:38.656)
Context Switch Count 4
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address 0x00401000
Stack Init 0 Current 9baebbf8 Base 9baec000 Limit 9bae9000 Call 0
Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
Thread は終了していることを考えると、TestApp.exe が Thread Handle を Close していないということが考えられます。
TestApp.exe を確認します。Handle Count が非常に大きいです。
1: kd> !process 8947ea58 0
PROCESS 8947ea58 SessionId: 1 Cid: 12ce80 Peb: 7ffdf000 ParentCid: 09dc
DirBase: 7ee534c0 ObjectTable: 9be74bb8 HandleCount: 1907884.
Image: TestApp.exe
このプロセスで開いている Handle を確認すると、大量の Thread Handle があることが確認できます。
1: kd> !handle 0 2 8947ea58
PROCESS 8947ea58 SessionId: 1 Cid: 12ce80 Peb: 7ffdf000 ParentCid: 09dc
DirBase: 7ee534c0 ObjectTable: 9be74bb8 HandleCount: 1907884.
Image: TestApp.exe
Handle table at 9bfaf490 with 1907884 entries in use
0004: Object: 93a7b0c0 GrantedAccess: 00000003 Entry: 8ea37008
Object: 93a7b0c0 Type: (8928c7c8) Directory
ObjectHeader: 93a7b0a8 (new version)
0008: Object: 8a7cc038 GrantedAccess: 00100020 Entry: 8ea37010
Object: 8a7cc038 Type: (89324f78) File
ObjectHeader: 8a7cc020 (new version)
000c: Object: 8a8f26a0 GrantedAccess: 001f0003 Entry: 8ea37018
Object: 8a8f26a0 Type: (8931e330) Event
ObjectHeader: 8a8f2688 (new version)
:
:
74acdc: Object: f850bd48 GrantedAccess: 001fffff Entry: f649b9b8
Object: f850bd48 Type: (8928c1e8) Thread
ObjectHeader: f850bd30 (new version)
74ace0: Object: f850ba60 GrantedAccess: 001fffff Entry: f649b9c0
Object: f850ba60 Type: (8928c1e8) Thread
ObjectHeader: f850ba48 (new version)
74ace4: Object: f850b778 GrantedAccess: 001fffff Entry: f649b9c8
Object: f850b778 Type: (8928c1e8) Thread
ObjectHeader: f850b760 (new version)
この環境で確認すると、1907864 個の Thread Object がわかりました。これらの Handle を Thread Object としてデコードすると、ほとんどが終了している Thread です。
よって、TestApp.exe が Thread Handle を Close してないことが原因になるので、アプリケーションの開発元に確認することで問題は解決です。
このように、Pool の問題はドライバが単独で大きく Pool を使用している場合、ほかの処理によって OS が使用している Pool が大量に確保される場合などがあり、状況によって調査の方針などを考える必要があります。
この確認を行うことによって、Pool の問題の切り分けも可能になるかと思います。
今後の、問題解決にぜひお役立てください。