출처 : http://darpangs.tistory.com/19
----------------------------------------------------------------------------------
참조 : http://www.codeproject.com/KB/cpp/MemoryLeak.aspx
1. gflags.exe 를 이용하여 메모리 할당 트레이싱 대상 프로그램을 세팅한다.
- gflags.exe /i MLeakTarget.exe +ust
: 프로세스 이름을 기준으로 트레이싱을 시작하는 듯.
2. WinDbg 를 이용해서 대상 프로세스에 attach 한다.
3. !heap -s 명령어를 입력한다.현재 사용 중인 heap 의 사용 내역이 보인다.
4. !heap -stat -h 00330000 명령어를 입력한다.
: 각 힙에 대한 핸들값을 마지막에 인자로 넣는다. ( 00330000 )
: 그러면 해당 힙의 사용 내역이 자세히 나온다.
: 위의 참조 글을 보면, 여기서 크기를 기준으로 필터링해서 다시 목록을 뽑고, 뽑아진 목록에서 해당 힙을 가리키는 유저모드 포인터 값이 나온다.
: 이 포인트 값을 이용해서 어떻게 할당된 메모리 인지 콜스택을 볼 수 있다. ( 이건 gflags.exe 에 의해 세팅되었기 때문에 가능하다. )
5. !heap -p -a 0x143fa303
: 이 명령을 입력하면, 인자로 넣어진 주소가 어떻게 allocation 되었는지 그 때의 콜 스택이 나오게된다.
: 적당한 디버깅 심벌들이 갖추어져 있다면, 어디서 할당된 놈인지 파악할 수 있을것이다.
6.. 원하는 주소값이 나온다면, ln 주소값 명령을 이용해서 해당되는 소스라인을 확보한다.
: ln 명령어 찾는다고 좀 헤멨음. ;;
----------------------------------------------------------------------------------------------
위의 참조 사이트에서 나오는 방법은 실제 필드에서는 저렇게 큰 메모리가 leak 되는 상황은 거의 없고, 실제로는 항상 작은 용량의 메모리 들이 leak 되기 때문에 별로 쓸모가 없다고 얘기가 나오고 있다.
뭐... 그럴수도 있을거 같고, 나의 경우엔...
현재 작업 중인 프로그램 코드내부에 메모리를 할당하고 해제하는 함수를 모조리 래핑해 놓은게 있다.
그래서 프로그램 종료 시에 할당되고 해제되지 않은 메모리의 주소값을 친절하게도 알려준다.
뭐 그 정도면 게임끝~ 위의 단계에서 바로 5번 단계의 명령어를 쓰면 필요한 콜 스택을 구할 수 있다.
--------------------------------------------------------------------------------------------
출처 : http://blog.daum.net/_blog/BlogTypeView.do?blogid=03gwy&articleno=15700730
상용툴을 사용하지 않고 WinDBG를 활용한 힙영역 메모리 누수 탐지에 관한 내용이 코드프로젝트 사이트에 있어서 복습할 겸 글을 써보기로 했습니다.
1. WinDBG 실행후 PDB와 실행이미지 경로 세팅
(실행파일은 계속적으로 메모리 누수가 일어나는 형태의 파일이어야 함. 간단한 소스를 만들면 됩니다)
2. 적당히 WinDbg로 실행하다가 브레이크를 건뒤 커맨드를 넣는다.
3. !heap -s 힙의 상태를 표시해줌
4. 다시 실행하고 적당히 시간이 지났을 때 브레이크 걸고 3번을 반복
5. 힙 핸들 중 이전에 비해 메모리 차지하는 비중이 큰 핸들 값을 조사
6 !heap -stat -h 12345678 12345678 핸들의 힙 상태를 보겠다는 의미
7. 이중에서 블럭 할당 값이 지나치게 많은 값이 메모리 누수 부분이라고 할 수 있겠습니다.
블럭 사이즈가 많이 있으므로 의심되는 블럭 크기의 값만 보기 위해
8. !heap -flt s 1fd4 블럭 크기가 1fd4인 값만 필터링해서 보여달라는 의미
9. 그러면 1f4d 크기로 할당된 리스트들이 가득 나옵니다. 각 항목중에 usrptr 값이 있습니다. 이 항목을 사용하면 이 값이 힙에 할당되기 까지의 스택 트레이스를 보여 줍니다.
10. !heap -p -a usrptr
--------------------------------------------------------------------------------------------
출처 : http://www.duck.pe.kr/m/post/173
00400000 00413000 image00400000 C (no symbols)
71710000 71794000 comctl32 (deferred)
77980000 77a1b000 oleaut32 (deferred)
77de0000 77e49000 user32 (deferred)
77e50000 77f32000 kernel32 (deferred)
77f40000 77f7c000 GDI32 (deferred)
77f80000 77ffc000 ntdll (pdb symbols) c:\websymbols\ntdll.pdb\41AFDCD61\ntdll.pdb
786f0000 7875f000 RPCRT4 (deferred)
796d0000 79735000 ADVAPI32 (deferred)
7cf00000 7cfef000 ole32 (deferred)
- .help : 메타명령어 (.명령) 도움말
- .hh : DEBUGGER.CHM 명령 띄우기.
- ~ Thread 관련 명령
Threads can only be specified in user mode. In kernel mode, the tilde (~) refers to a processor.
Also note that many commands can be preceded by a thread symbol. For an explanation of a tilde (~) followed by a command, see the entry for the command itself.
Here are examples of the use of this command. The following command will display all threads:
The following command will also display all threads:
The following command will display the currently active thread:
The following command will display the thread that originally caused the exception (or that was active when the debugger attached to the process):
The following command will display thread number 2:
Here is an example of the output of this command:
0 id: 4dc.470 Suspend: 0 Teb 7ffde000 Unfrozen
. 1 id: 4dc.534 Suspend: 0 Teb 7ffdd000 Unfrozen
# 2 id: 4dc.5a8 Suspend: 0 Teb 7ffdc000 Unfrozen
On the first line of this example, 0 is the decimal thread number; 4DC is the hexadecimal process ID, 470 is the hexadecimal thread ID, 0x7FFDE000 is the address of the TEB, and Unfrozen is the thread status. The period (.) before thread 1 means this is the current thread. The number sign (#) before thread 2 means this thread was the one that originally caused the exception or it was ctive when the debugger attached to the rocess.
- LD : (load Symbols)
0:000> ld ole32
Symbols loaded for ole32
0:000> lm
start end module name
00400000 00413000 image00400000 C (no symbols)
71710000 71794000 comctl32 (deferred)
77980000 77a1b000 oleaut32 (deferred)
77de0000 77e49000 user32 (deferred)
77e50000 77f32000 kernel32 (deferred)
77f40000 77f7c000 GDI32 (deferred)
77f80000 77ffc000 ntdll (pdb symbols) c:\websymbols\ntdll.pdb\41AFDCD61\ntdll.pdb
786f0000 7875f000 RPCRT4 (deferred)
796d0000 79735000 ADVAPI32 (deferred)
7cf00000 7cfef000 ole32 (pdb symbols) c:\websymbols\ole32.pdb\42CABEE01\ole32.pdb
0:000> ld kernel32
Symbols loaded for kernel32
0:000> lm
start end module name
00400000 00413000 image00400000 C (no symbols)
71710000 71794000 comctl32 (deferred)
77980000 77a1b000 oleaut32 (deferred)
77de0000 77e49000 user32 (deferred)
77e50000 77f32000 kernel32 (pdb symbols) c:\websymbols\kernel32.pdb\4498D8981\kernel32.pdb
77f40000 77f7c000 GDI32 (deferred)
77f80000 77ffc000 ntdll (pdb symbols) c:\websymbols\ntdll.pdb\41AFDCD61\ntdll.pdb
786f0000 7875f000 RPCRT4 (deferred)
796d0000 79735000 ADVAPI32 (deferred)
7cf00000 7cfef000 ole32 (pdb symbols) c:\websymbols\ole32.pdb\42CABEE01\ole32.pdb
0:000> ld *
Symbols already loaded for image00400000
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINNT\system32\comctl32.dll -
Symbols loaded for comctl32
Symbols loaded for oleaut32
Symbols loaded for user32
Symbols already loaded for kernel32
Symbols loaded for GDI32
Symbols already loaded for ntdll
Symbols loaded for RPCRT4
Symbols loaded for ADVAPI32
Symbols already loaded for ole32
0:000> lm
start end module name
00400000 00413000 image00400000 C (no symbols)
71710000 71794000 comctl32 (export symbols) C:\WINNT\system32\comctl32.dll
77980000 77a1b000 oleaut32 (pdb symbols) c:\websymbols\oleaut32.pdb\3DF8FF78\oleaut32.pdb
77de0000 77e49000 user32 (pdb symbols) c:\websymbols\user32.pdb\4211D1FD1\user32.pdb
77e50000 77f32000 kernel32 (pdb symbols) c:\websymbols\kernel32.pdb\4498D8981\kernel32.pdb
77f40000 77f7c000 GDI32 (pdb symbols) c:\websymbols\gdi32.pdb\43B3D6741\gdi32.pdb
77f80000 77ffc000 ntdll (pdb symbols) c:\websymbols\ntdll.pdb\41AFDCD61\ntdll.pdb
786f0000 7875f000 RPCRT4 (pdb symbols) c:\websymbols\rpcrt4.pdb\41E6468F7\rpcrt4.pdb
796d0000 79735000 ADVAPI32 (pdb symbols) c:\websymbols\advapi32.pdb\4253BAFE1\advapi32.pdb
7cf00000 7cfef000 ole32 (pdb symbols) c:\websymbols\ole32.pdb\42CABEE01\ole32.pdb
start end module name
77f40000 77f7c000 GDI32 (pdb symbols) c:\websymbols\gdi32.pdb\43B3D6741\gdi32.pdb
Loaded symbol image file: c:\websymbols\gdi32.dbg\43B3E1B73c000\gdi32.dbg
Image path: C:\WINNT\system32\GDI32.dll
Image name: GDI32.dll
Timestamp: Thu Dec 29 22:16:39 2005 (43B3E1B7)
CheckSum: 0003E315
ImageSize: 0003C000
File version: 5.0.2195.7073
Product version: 5.0.2195.7073
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0409.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft(R) Windows (R) 2000 Operating System
InternalName: gdi32
OriginalFilename: gdi32
ProductVersion: 5.00.2195.7073
FileVersion: 5.00.2195.7073
FileDescription: GDI Client DLL
LegalCopyright: Copyright (C) Microsoft Corp. 1981-1999
심볼서버
To use the Microsoft Symbol Server
1. Make sure you have installed the latest version of Debugging Tools for Windows.
2. Start a debugging session.
3. Decide where to store the downloaded symbols (the "downstream store"). This can be a local drive or a UNC path.
4. Set the debugger symbol path as follows, substituting your downstream store path for DownstreamStore.
SRV*DownstreamStore*http://msdl.microsoft.com/download/symbols
예) SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
장애의종류
Crash: Heap Corrupt, AV(Access Violation)
Hang: Deal lock, Orphaned thread, contention..
Leak: Memory Leak
장애 종류에 따른 대처
Crash:
Access Violation
- adplus.vbs > CDB를 사용
- Tlist로 process id확인 (id)
- Adplus.vbs –crash –p pid : Crash가 발생하면 메모리 덤프를 생성
Heap Corrupt
- page heap: heap영역에 할당된 메모리의 영역을 체크함. 범위를 초과하는 경우 프로세스를 종료시킴.
- gflags.exe 성능을 떨어뜨릴 수 있으면 덤프를 뜨고나면 disable 시켜야 한다.
http://support.microsoft.com/kb/286470/ : 마이크로 소프트의 페이지
시스템 범위 페이지 힙에 GFlags 사용
GFlags 도구는 시스템 범위 페이지 힙을 설정하는 데 사용됩니다. GFlags 명령을 적용하려면 명령을 실행한 후 컴퓨터를 다시 시작해야 합니다.
시스템 범위 일반 페이지 힙을 설정하려면 다음과 같이 하십시오. 1. 명령줄에 다음을 입력합니다.
gflags -r +hpa
2. 컴퓨터를 다시 시작합니다.
시스템 범위 일반 페이지 힙을 해제하려면 다음과 같이 하십시오. 1. 명령줄에 다음을 입력합니다.
gflags -r -hpa
2. 컴퓨터를 다시 시작합니다.
단일 프로세스 페이지 힙에 GFlags 사용
특정 프로세스를 모니터링하기 위해 페이지 힙을 사용할 수 있도록 설정할 수 있습니다. 이렇게 하려면 다음 단계를 수행하십시오.
1. 명령 프롬프트에서 디버그 도구를 설치한 디렉터리로 변경합니다.
2. 명령 프롬프트에서 다음을 입력한 후 Enter 키를 누릅니다.
Gflags.exe –p /enable lsass.exe
참고 lsass.exe는 Pageheap 도구로 모니터링할 프로세스의 이름을 나타냅니다.
3. 페이지 힙 모니터링이 더 이상 필요하지 않으면 모니터링을 해제합니다. 수신 대기 포트의 목록을 얻으려면 프롬프트에서 다음 명령을 입력하고 Enter 키를 누릅니다.
Gflags.exe -p /disable lsass.exe
참고 lsass.exe는 Pageheap 도구로 모니터링할 프로세스의 이름을 나타냅니다.
4. 현재 Pageheap 확인을 사용하는 모든 프로그램을 표시하려면 명령 프롬프트에서 다음을 입력한 다음 Enter 키를 누릅니다.
Gflags.exe –p
* Hang 장애 시
Deadlock, 무한 루프(while(true){ … } )등의 로직 관련 문제.
- Hang dump를 받는다. Hang dump는 두 번 이상 받아서 체크하도록 한다.
Adplus.vbs –hang –p pid(process id)
- Deallock이나 lock이 문제가 되는 경우에는 Windbg로 !lock을 통해서 현재 lock을 가장 많이 잡고 있는 Thread등을 조사하여 본다.
* 메모리 누수
Windows 2000/XP, 2003 이상의 경우
- DebugDiag라는 메모리 누수 감지 공개소프트웨어를 사용하고, perfmon(성능 모니터)에서 process의 private bytes, virtual Bytes를 살펴보아서 private bytes의 지속적인 증가, virtual Bytes의 계단식 증가를 살펴보도록 한다.
'UTIL > Debugging' 카테고리의 다른 글
Windbg를 이용한 메모리 릭 검사(영문) (0) | 2014.12.26 |
---|---|
windbg 사용법 (WinDebug) (0) | 2014.05.26 |
gdb 사용법 안내 (0) | 2012.08.01 |
dbx와 core 파일 분석 및 사용법 (0) | 2012.08.01 |