본문 바로가기

Windows Platform

Testlimit.exe를 이용한 Handle leak 테스트

최근 몇일 사이 Windows server 2008과 2003 에서 특정 프로세의 handle Leak으로 시스템의 성능이 매우 느려진 사례가 있었습니다. 

물론 다른 여러 현상에 의해서 성능 이슈가 발생 할 수 있기는 하지만 시스템의 성능 이슈가 발생하면 언제나 가장 먼저 확인 해 봐야 하는 것이 작업 관리자에서 『커널 메모리(kernel memory)』부분과 『Handle(핸들)』 부분이 아닌가 생각합니다.
 x86의 시스템에 발생하는 시스템 hang이나 성능 이슈의 대부분은 커널 리소스 문제 부족 때문이었으며 이 것을 가장 빨리 확인 해 볼 수 있는 방법이 작업 관리자의 커널 리소스를 확인 해 보는 것입니다. 물론 x64 머신의 경우에도 성능 이슈가 느껴진다면 이 부분을 확인 해 볼 필요가 있습니다^^

관련해서 최근 발생한 프로세스의 handle leak 현상을 testlimit.exe 프로그램을 이용해서 재현을 해 보았습니다.
먼저 정상적인 시스템의 작업 관리자를 보면 아래와 같이 커널 리소스의 사용량이 얼마 되지 않습니다.
handles -> 5539
Paged pool -> 10616kb / Nonpaged pool 89222kb입니다
.

자 이제 Testlimit.exe라는 tool을 이용해서 해당 시스템에 handle leak을 유발 해 보도록 하겠습니다.먼저 아래 파일을 다운로드 합니다.
testlimit.exe  다운로드
http://download.sysinternals.com/Files/Testlimit.zip

위 링크를 통해 다운 받은 파일의 압축을 해제 후  C:\ 루트에 copy를 하였습니다.  그리고는 아래와 같은 명령프롬프트를 실행 후  명령어를 실행 합니다.

맨 마지막에 부여한 -c 옵션은 실제 생성할 핸들 수를 지정 하는 옵션입니다 .이 옵션을 사용하지 않을 경우에는 시스템에서 핸들을 생성 할 수 있을 때 까지 계속 생성합니다.

C:\>Testlimit.exe -h -u -c 1000000      // 백만개의핸들을 생성 합니다.
Testlimit v5.04 - test Windows limits
By Mark Russinovich - www.sysinternals.com

Creating handles...
Created 1000000 handles. Lasterror: 0
The operation completed successfully.


** 옵션에 대한 자세한 설명은 testlimit.exe /?  실행 하신 후 확인 할 수 있습니다
 -a       Leak Address Windowing Extensions (AWE) memory in
          specified MBs (default is 1).
 -c       Count of number of objects to allocate (default is as many as possible).
            This must be the last option specified.
 -d       Leak and touch memory in specified MBs (default is 1).
 -g       Create GDI handles of specified size (default 1 byte).
          Specify a size of 0 to cause GDI object exhaustion.
 -h       Create handles. Specify -u to also allocate file objects.
 -i       Exhaust USER desktop heap.
 -l       VirtualLock memory in specified MBs (default is 1).
 -m       Leak memory in specified MBs (default is 1).
 -p       Create processes - add -n to set min working set. Add -n to
          set min working set of processes to smallest.
 -r       Reserve memory in specified MBs (default is 1).
 -s       Leak shared memory in specified MBs (default is 1).
 -t       Create threads - add -n to specfy minimum stack reserve (in KB).
 -u       Create USER handles to menus.
 -w       Reset working set minimum to highest possible value.

위 명령어 실행 후 작업 관리자를 확인 하면 아래와 같이 hanldes와 paged pool 그리고 nonpaged pool의 사이즈가 현격하게 변화 된 것을 확인 할 수 있습니다.

핸들의 수가 백만개가 늘었으며 커널 리소스 또한 많이 상승이 되었습니다.

실제 어떤 프로세스가 많은 핸들을 open 한 것인지 확인하기 위해 아래와 작업관리자의 메뉴에서 [보기 ]-> [컬럼 선택]을 클릭 합니다. 

컬럼을 선택하는 화면이 팝업 되면 아래와 같이 핸들 카운터 (Handle count) 체크 박스에 체크를 합니다.

그리고 프로세스 탭을 선택 한 후  핸들(handles) 를 클릭하여 가장 많은 handle을 open한  프로세스를 확인 합니다.
Cmd를 통해서 실행한 testlimit.exe 프로세스가 오픈한 핸들이 1,000,018  임을 확인 할 수 있습니다.


만약 해당 서버에 Windows debugging tool이 설치 되어 있고  로컬 커널 디버깅을 실행 할 수 있는 상태라면  아래와 같이 커널 리소스를 추가로 상세하게 확인 할 수 있습니다.

*심볼을  fix 한 후 reload 한 후 아래 명령어를 실행 합니다. 

lkd> !vm 1  -> nonpaged pool과 paged pool의 현재 사용량과 최대 값을 확인 합니다.

*** Virtual Memory Usage ***
 Physical Memory:      262008 (   1048032 Kb)
 Page File: \??\C:\pagefile.sys
   Current:   1572864 Kb  Free Space:   1565416 Kb
   Minimum:   1572864 Kb  Maximum:      3145728 Kb
 Available Pages:      139839 (    559356 Kb)
 ResAvail Pages:       200212 (    800848 Kb)
 Locked IO Pages:         114 (       456 Kb)
 Free System PTEs:      51647 (    206588 Kb)
 Free NP PTEs:           2779 (     11116 Kb)
 Free Special NP:           0 (         0 Kb)
 Modified Pages:          541 (      2164 Kb)
 Modified PF Pages:       541 (      2164 Kb)
 NonPagedPool Usage:    49084 (    196336 Kb) // Nonpaged pool의 현재 사용량
 NonPagedPool Max:      51960 (    207840 Kb)
// Nonpaged pool의 최대 값
 ********** Excessive NonPaged Pool Usage *****  // Nonpaged pool이 거의 다 소진 되었음을 확인 할 수 있음

 PagedPool 0 Usage:      3711 (     14844 Kb)
 PagedPool 1 Usage:     17391 (     69564 Kb)
 PagedPool 2 Usage:     16942 (     67768 Kb)
 PagedPool Usage:       38044 (    152176 Kb)
 PagedPool Maximum:     70656 (    282624 Kb)
 Session Commit:          160 (       640 Kb)
 Shared Commit:          2196 (      8784 Kb)
 Special Pool:              0 (         0 Kb)
 Shared Process:         2257 (      9028 Kb)
 PagedPool Commit:      38051 (    152204 Kb)
 Driver Commit:          1238 (      4952 Kb)
 Committed pages:       98775 (    395100 Kb)
 Commit limit:         632325 (   2529300 Kb) 

lkd> !poolused /t5 2 ->nonpaged pool을 가장 많이 사용한  top 5 pool tag 확인
Sorting by NonPaged Pool Consumed

Pool Used:
NonPaged Paged
Tag Allocs Used Allocs Used
File 1001336 152203760 0 0 File objects  --> File 이라는  tag가  약 150M 이상의 nonpaged pool을 사용함
LSwi 1 2576384 0 0 initial work context
TCPt 29 1422336 0 0 TCP/IP network protocol , Binary: TCP
MmCm 11 581904 0 0 Calls made to MmAllocateContiguousMemory , Binary: nt!mm
LSwr 128 416768 0 0 raw work context
TOTAL 1020161 161159168 2029611 155126792

lkd> !process 0 0 testlimit.exe //
PROCESS 85933d88  SessionId: 1  Cid: 06a4    Peb: 7ffd4000  ParentCid: 0410
    DirBase: 3c1e6380  ObjectTable: e11ef3a0  HandleCount: 1000018.
    Image: Testlimit.exe

실제 수백만개의 핸들이 오픈 되고 커널 리소스가 부족 해진 상황에서는 작업 관리자를 확인 하는 것 조차 어려울 수 도 있습니다만 만약에라도 확인이 가능 한 상태라면 반드시 커널 리소스와 핸들을 반드시 먼저 확인 해 보시기 바랍니다.

[참고 자료]
Pushing the Limits of Windows: Handles
http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx

Understanding Pool Consumption and Event ID: 2020 or 2019
http://blogs.msdn.com/b/ntdebugging/archive/2006/12/18/understanding-pool-consumption-and-event-id_3a00_--2020-or-2019.aspx

감사합니다.