다음을 통해 공유


애플리케이션 검증 도구 - 애플리케이션 검증 도구 디버깅 중지

디버거 설치 및 설정

일부 애플리케이션 검증 도구 작업으로 인해 예외가 발생할 수 있습니다. 애플리케이션 검증 도구 자체가 첫 번째 예외를 처리하므로 두 번째 기회에 이러한 예외를 catch하도록 디버거를 설정해야 합니다.

발생한 예외는 다음 세 가지 유형입니다.

  • 힙 옵션이 힙 버퍼 오버런을 감지하면 액세스 위반 예외(0xC0000005)가 생성됩니다. 경우에 따라 시스템 경로 사용 확인 옵션을 사용하면 액세스 위반도 발생할 수 있습니다.

  • 잘못된 핸들 사용 감지 옵션이 잘못된 핸들 작업을 검색하면 잘못된 핸들 예외(0xC0000008)가 생성됩니다.

  • 적절한 스택 확인 옵션이 초기 스택이 너무 짧다는 것을 감지하면 스택 오버플로 예외(0xC00000FD)가 생성됩니다.

이러한 이벤트를 준비하는 한 가지 방법은 다음과 같이 명령줄에서 디버거를 시작하는 것입니다.

windbg -xd av -xd ch -xd sov ApplicationCommandLine

또는

cdb -xd av -xd ch -xd sov ApplicationCommandLine

디버거를 이미 시작한 경우 sxd(예외 설정) 명령을 사용하여 모든 액세스 위반, 잘못된 핸들 및 스택 오버플로를 두 번째 예외로 catch할 수 있습니다.

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

이론적으로는 커널 디버거를 통해 애플리케이션 검증 도구 제어가 가능합니다. 그러나 권장되지는 않습니다. .process 및 .pagein 명령을 자주 사용해야 하지만 사용자 모드 디버거를 사용하는 것보다 더 많은 기능을 제공하지 않습니다.

디버깅 도구 설치

최신 버전의 도구를 다운로드하려면 Windows용 디버깅 도구 다운로드를 참조하세요.

User-Mode 디버깅을 위한 하드웨어 구성

사용자 모드 디버깅은 일반적으로 단일 컴퓨터에서 수행됩니다. 디버거는 실패한 애플리케이션과 동일한 컴퓨터에서 실행됩니다.

이 경우 특정 하드웨어 설정이 필요하지 않습니다. 이 항목 전체에서 호스트 컴퓨터 및 대상 컴퓨터라는 용어는 이 경우에 서로 교환할 수 있습니다.

User-Mode 디버깅을 위한 소프트웨어 구성

기본 User-Mode 구성 - 사용자 모드 디버깅을 시작하려면 먼저 필요한 기호 파일을 다운로드하고 특정 환경 변수를 설정해야 합니다.

기호 파일

디버깅 중인 사용자 모드 프로세스에 대한 기호 파일을 다운로드해야 합니다. 작성한 애플리케이션인 경우 전체 기호 파일을 사용하여 빌드해야 합니다. 상용 애플리케이션인 경우 웹 서버에서 또는 다운로드를 위해 기호 파일을 사용할 수 있습니다. 제조업체에 문의하세요.

원격 디버깅을 수행하는 경우 기호 파일 위치는 사용 중인 메서드에 따라 달라집니다.

  • 디버거를 통해 원격 디버깅을 수행하는 경우 디버깅 서버가 있는 컴퓨터에 기호 파일이 있어야 합니다.

  • remote.exe 통해 원격 디버깅을 수행하는 경우 디버거가 있는 컴퓨터에 기호 파일이 있어야 합니다.

  • 프로세스 서버 또는 KD 연결 서버를 통해 원격 디버깅을 수행하는 경우 기호 파일은 스마트 클라이언트가 있는 컴퓨터에 있어야 합니다.

  • 커널 디버거에서 사용자 모드 디버거를 제어하는 경우 기호 파일은 두 컴퓨터 모두에 있어야 합니다.

환경 변수 구성

디버거는 다양한 환경 변수를 사용하여 여러 가지 중요한 설정을 나타냅니다.

디버거에 대한 자세한 내용은 Windows 디버깅을 사용하는 시작 참조하세요.

명령줄을 사용하여 디버거를 사용하여 애플리케이션 검증 도구 구성

Application Verifier를 구성하려면 CDB 또는 NTSD 명령줄을 사용할 수 있습니다.

다음 명령줄을 사용합니다.

cdb OtherOptions -vf:Flags Target

여기서 Target은 대상 애플리케이션의 이름이고 Flags는 이 대상에 적용할 원하는 애플리케이션 검증 도구 옵션을 지정합니다.

플래그는 원하는 옵션을 나타내는 비트의 합계여야 합니다. 개별 비트 값은 다음과 같습니다.

플래그 값 의미
00000001 힙 검사
00000004 검사 처리
00000008 낮은 리소스 SIM 검사
00000020 TLS 검사
00000040 더티 스택
00000200 위험한 API
00001000 예외 검사
00002000 메모리 검사
00020000 기타 검사
00040000 잠금 검사

!avrf를 사용하여 디버깅

!avrf 확장은 애플리케이션 검증 도구의 설정을 제어하고 애플리케이션 검증 도구에서 생성한 다양한 출력을 표시합니다. !arvrf 확장에 대한 자세한 내용은 디버거 문서의 !avrf 를 참조하세요.

Syntax

!avrf

매개 변수가 없는 !avrf 명령은 애플리케이션 검증 도구 설정과 현재 및 이전 애플리케이션 검증 도구에 대한 정보가 있는 경우 중단되는 정보를 표시합니다.

!avrf –vs { Length | -aAddress }

가상 공간 작업 로그를 표시합니다. Length는 가장 최근부터 표시할 레코드 수를 지정합니다. 주소는 가상 주소를 지정합니다. 이 가상 주소를 포함하는 가상 작업의 레코드가 표시됩니다.

!avrf -hp { Length | -a Address }

힙 작업 로그를 표시합니다. 주소는 힙 주소를 지정합니다. 이 힙 주소를 포함하는 힙 작업의 레코드가 표시됩니다.

!avrf -cs { Length | -a Address }

중요한 섹션 삭제 로그를 표시합니다. Length는 가장 최근부터 표시할 레코드 수를 지정합니다. 주소는 중요한 섹션 주소를 지정합니다. 주소가 지정되면 특정 중요 섹션에 대한 레코드가 표시됩니다.

!avrf -dlls [ Length ]

DLL 로드/언로드 로그를 표시합니다. Length는 가장 최근부터 표시할 레코드 수를 지정합니다.

!avrf -trm

종료되고 일시 중단된 모든 스레드의 로그를 표시합니다.

!avrf -ex [ Length ]

예외 로그를 표시합니다. 애플리케이션 검증 도구는 애플리케이션에서 발생하는 모든 예외를 추적합니다.

!avrf -threads [ ThreadID ]

대상 프로세스의 스레드에 대한 정보를 표시합니다. 자식 스레드의 경우 부모에서 지정한 스택 크기 및 CreateThread 플래그도 표시됩니다. 스레드 ID를 제공하면 해당 특정 스레드에 대한 정보만 표시됩니다.

!avrf -tp [ ThreadID ]

스레드 풀 로그를 표시합니다. 이 로그에는 스레드 선호도 마스크 변경, 스레드 우선 순위 변경, 스레드 메시지 게시, COM 초기화 및 스레드 풀 콜백 내에서 COM 초기화 취소와 같은 다양한 작업에 대한 스택 추적이 포함될 수 있습니다. 스레드 ID를 제공하면 해당 특정 스레드에 대한 정보만 표시됩니다.

!avrf -srw [ Address | Address Length ] [ -stats ]

SRW(슬림 판독기/기록기) 로그를 표시합니다. 주소를 지정하면 해당 SRW 잠금 주소와 관련된 레코드가 표시됩니다. Length를 주소와 함께 지정하면 해당 주소 범위 내의 모든 SRW 잠금이 표시됩니다. -stats 옵션은 SRW 잠금 통계를 덤프합니다.

!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]

미해결 리소스 로그를 표시합니다. 이러한 리소스는 지정된 시점에서 누출될 수도 있으며 누출되지 않을 수도 있습니다. ModuleName(확장 포함)을 지정하면 지정된 모듈의 모든 미해결 리소스가 표시됩니다. ResourceType을 지정하면 해당 특정 리소스 유형의 미해결 리소스가 표시됩니다. 주소 지정은 해당 주소를 사용하여 미해결 리소스의 레코드를 덤프합니다. ResourceType은 다음 중 하나일 수 있습니다.

  • 힙: Win32 힙 API를 사용하여 힙 할당 표시
  • 로컬: 로컬/전역 할당 표시
  • CRT: CRT API를 사용하여 할당 표시
  • 가상: 가상 예약 표시
  • BSTR: BSTR 할당 표시
  • 레지스트리: 레지스트리 키가 열립니다.
  • 전원: 전원 알림 개체 표시
  • 핸들: 스레드, 파일 및 이벤트 핸들 할당을 표시합니다.

!avrf –trace TraceIndex

지정된 추적 인덱스에 대한 스택 추적을 표시합니다. 일부 구조체는 이 16비트 인덱스 번호를 사용하여 스택 추적을 식별합니다. 이 인덱스 는 스택 추적 데이터베이스 내의 위치를 가리킵니다. 이러한 구조를 분석하는 경우 이 구문이 유용합니다.

!avrf -cnt

전역 카운터 목록을 표시합니다.

!avrf -brk [ BreakEventType ]

이 명령이 break-event 명령임을 지정합니다. 가 추가 매개 변수 없이 사용되는 경우 !avrf -brk 중단 이벤트 설정이 표시됩니다. BreakEventType은 break 이벤트의 형식 번호를 지정합니다. 가능한 형식 목록은 을 사용합니다 !avrf -brk.

!avrf -flt [ EventTypeProbability ]

이 명령이 오류 주입 명령임을 지정합니다. 가 추가 매개 변수 없이 사용되는 경우 !avrf -flt 현재 오류 주입 설정이 표시됩니다. EventType은 이벤트의 형식 번호를 지정합니다. 확률은 이벤트가 실패할 빈도를 지정합니다. 0에서 1,000,000(0xF4240) 사이의 정수일 수 있습니다.

!avrf -flt break EventType

이 오류가 삽입될 때마다 애플리케이션 검증 도구가 디버거에 침입하도록 합니다.

!avrf -flt stacks Length

가장 최근의 내결함성 작업에 대한 스택 추적의 길이 수를 표시합니다.

!avrf -trg [ StartEnd | dll Module | all ]

대상 범위 명령임을 지정합니다. 추가 매개 변수 없이 -trg를 사용하면 현재 대상 범위가 표시됩니다. 시작은 대상 범위 또는 제외 범위의 시작 주소를 지정합니다. 끝은 대상 범위 또는 제외 범위의 끝 주소를 지정합니다. 모듈은 대상으로 지정하거나 제외할 모듈의 이름을 지정합니다. 모듈에는 .exe 또는 .dll 확장을 포함하여 전체 모듈 이름이 포함되어야 합니다. 경로 정보는 포함되지 않아야 합니다. 모든 대상 범위를 지정하면 모든 대상 범위 또는 제외 범위가 다시 설정됩니다.

!avrf -skp [ StartEnd | dll Module | all | Time ]

제외 범위 명령임을 지정합니다. 시작은 대상 범위 또는 제외 범위의 시작 주소를 지정합니다. 끝은 대상 범위 또는 제외 범위의 끝 주소를 지정합니다. 모듈은 대상으로 지정하거나 제외할 모듈의 이름을 지정합니다. 모듈에는 .exe 또는 .dll 확장을 포함하여 전체 모듈 이름이 포함되어야 합니다. 경로 정보는 포함되지 않아야 합니다. 모든 대상 범위를 지정하면 모든 대상 범위 또는 제외 범위가 다시 설정됩니다. 시간을 지정하면 실행이 다시 시작된 후 시간 밀리초 동안 모든 오류가 표시되지 않습니다.

다음은 디버거에서 !avrf 명령에서 제공하는 출력입니다.

0:000> !avrf
Application verifier settings (816431A7):

   - full page heap
   - COM
   - RPC
   - Handles
   - Locks
   - Memory
   - TLS
   - Exceptions
   - Threadpool
   - Leak
   - SRWLock

No verifier stop active.

Note: Sometimes bugs found by verifier manifest themselves as raised
exceptions (access violations, stack overflows, invalid handles), 
and it is not always necessary to have a verifier stop.

!avrf 확장 설명

매개 변수 없이 !avrf 확장을 사용하면 현재 애플리케이션 검증 도구 옵션이 표시됩니다.

!avrf 확장은 디버거의 Exts.dll 사용합니다.

애플리케이션 검증 도구 중지가 발생한 경우 매개 변수가 없는 !avrf 확장은 중지의 특성과 그 원인을 표시합니다.

ntdll.dll 및 verifier.dll 기호가 없으면 !avrf 확장에서 오류 메시지가 생성됩니다.

연속 가능 및 비지속적 중지

연속 가능한 중지 디버깅

다음은 잘못된 핸들 사용 감지 옵션에 의해 발생한 잘못된 핸들 예외의 예입니다.

먼저 다음 메시지가 나타납니다.

Invalid handle - code c0000008 (first chance)

===================================================

VERIFIER STOP 00000300: pid 0x558: invalid handle exception for current stack trace

        C0000008 : Exception code.

        0012FBF8 : Exception record. Use .exr to display it.

        0012FC0C : Context record. Use .cxr to display it.

        00000000 :

===================================================

This verifier stop is continuable.

After debugging it use 'go' to continue.

===================================================

Break instruction exception - code 80000003 (first chance)

eax=00000000 ebx=6a27c280 ecx=6a226447 edx=0012fa4c esi=00942528 edi=6a27c260

eip=6a22629c esp=0012facc ebp=0012faf0 iopl=0         nv up ei pl zr na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!DbgBreakPoint:

6a22629c cc               int     3

이 애플리케이션 검증 도구 중지를 계속할 수 있다는 메시지가 표시됩니다. 무슨 일이 일어났는지 이해한 후에는 대상 애플리케이션을 계속 실행할 수 있습니다.

먼저 !avrf 확장을 사용해야 합니다. 이렇게 하면 현재 오류에 대한 정보가 제공됩니다.

0:000> !avrf

Global flags: 00000100

Application verifier global flag is set.

Application verifier settings (00000004):

   - no heap checking enabled!

   - handle checks

Page heap is not active for this process.

Current stop 00000300 : c0000008 0012fbf8 0012fc0c 00000000 .

    Using an invalid handle (either closed or simply bad).

이 디스플레이의 마지막 줄에는 문제가 요약됩니다.

이 시점에서 일부 로그를 살펴볼 수 있습니다. 완료되면 g(Go) 명령을 사용하여 애플리케이션을 다시 시작합니다.

0:000> g

## Debugging a Non-Continuable Stop

Here is an example of an access violation that has been raised by the page heap option.

First, the following message appears:

Access violation - code c0000005 (first chance)

===================================================

VERIFIER STOP 00000008: pid 0x504: exception raised while verifying block header

        00EC1000 : Heap handle

        00F10FF8 : Heap block

        00000000 : Block size

        00000000 :

===================================================

This verifier stop is not continuable. Process will be terminated when you use the 'go' debugger command.

===================================================

Break instruction exception - code 80000003 (first chance)

eax=00000000 ebx=00000000 ecx=6a226447 edx=0012fab7 esi=00f10ff8 edi=00000008

eip=6a22629c esp=0012fb5c ebp=0012fb80 iopl=0         nv up ei pl zr na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!DbgBreakPoint:

6a22629c cc               int     3

이 경우 이 애플리케이션 검증 도구 중지를 계속할 수 없다는 메시지가 표시됩니다. 이 오류는 프로세스가 계속 실행되기에는 너무 심각하며 Application Verifier가 프로세스를 회수할 방법이 없습니다.

!avrf 확장을 사용하여 현재 오류에 대한 정보를 제공할 수 있습니다.

0:000> !avrf

Global flags: 02000100

Application verifier global flag is set.

Page heap global flag is set.

Application verifier settings (00000001):

   - full page heap

Page heaps active in the process (format: pageheap, lightheap, flags):

    00941000 , 00a40000 , 3 (pageheap traces )

    00b41000 , 00c40000 , 3 (pageheap traces )

    00cb1000 , 00db0000 , 3 (pageheap traces )

    00ec1000 , 00fc0000 , 3 (pageheap traces )

Current stop 00000008 : 00ec1000 00f10ff8 00000000 00000000 .

    Corrupted heap block.

이 디스플레이의 마지막 줄에는 문제가 요약됩니다.

이 시점에서 일부 로그를 살펴볼 수도 있습니다. 이 시점에서 .restart(대상 애플리케이션 다시 시작) 명령을 사용할 수 있습니다. 또는 애플리케이션 검증 도구 세션을 종료하고 코드의 버그 수정을 시작하는 것이 좋습니다.

중요한 섹션 오류 디버깅

!cs 디버거 확장

!cs는 사용자 모드 디버거와 커널 디버거 모두에서 사용하여 현재 프로세스의 중요한 섹션에 대한 정보를 표시할 수 있습니다. !cs 확장에 대한 자세한 내용은 디버거 문서의 !cs 를 참조하세요.

특히 ntdll.dll 경우 형식 정보와 기호를 일치해야 합니다.

이 확장의 구문은 다음과 같습니다.

!cs [-s] - 현재 프로세스의 모든 활성 중요 섹션을 덤프합니다.

!cs [-s] 주소 - 이 주소에서 중요한 섹션을 덤프합니다.

!cs [-s] -d address - 이 주소의 DebugInfo에 해당하는 덤프 중요 섹션입니다.

-s는 사용 가능한 경우 중요한 섹션 초기화 스택 추적을 덤프합니다.

예:

주소를 사용하여 중요한 섹션에 대한 정보 덤프

0:001> ! cs 0x7803B0F8

Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo          = 0x6A262080
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

초기화 스택 추적을 포함하여 주소를 사용하여 중요한 섹션에 대한 정보를 덤프합니다.

0:001> !cs -s 0x7803B0F8

Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo          = 0x6A262080
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5

디버그 정보 주소를 사용하여 중요한 섹션에 대한 정보 덤프

0:001> !cs -d 0x6A262080

DebugInfo          = 0x6A262080
Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

초기화 스택 추적을 포함하여 디버그 정보 주소를 사용하여 중요한 섹션에 대한 정보를 덤프합니다.

0:001> !cs -s -d 0x6A262080

DebugInfo          = 0x6A262080
Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE

현재 프로세스의 모든 활성 중요 섹션에 대한 정보 덤프

0:001> !cs

-----------------------------------------

DebugInfo          = 0x6A261D60
Critical section   = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)
LOCKED
LockCount          = 0x0
OwningThread       = 0x460
RecursionCount     = 0x1
LockSemaphore      = 0x0
SpinCount          = 0x0
-----------------------------------------

DebugInfo          = 0x6A261D80
Critical section   = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)
NOT LOCKED
LockSemaphore      = 0x7FC
SpinCount          = 0x0
-----------------------------------------

DebugInfo          = 0x6A262600
Critical section   = 0x6A26074C (ntdll!LoaderLock+0x0)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
.....

초기화 스택 추적을 포함하여 현재 프로세스의 모든 활성 중요 섹션에 대한 정보를 덤프합니다.


0:001> !cs -s

...

-----------------------------------------

DebugInfo          = 0x6A261EA0
Critical section   = 0xA8001C (+0xA8001C)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
No stack trace saved

-----------------------------------------

DebugInfo          = 0x6A261EC0
Critical section   = 0x6A263560 (ntdll!RtlpDphTargetDllsLock+0x0)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
No stack trace saved

-----------------------------------------

DebugInfo          = 0x6A261EE0
Critical section   = 0xA90608 (+0xA90608)
NOT LOCKED
LockSemaphore      = 0x7EC
SpinCount          = 0x0
Stack trace for DebugInfo = 0x6A261EE0:

0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A20B0DC: ntdll!CsrpConnectToServer+0x1BE
0x6A20B2AA: ntdll!CsrClientConnectToServer+0x148
0x77DBE83F: KERNEL32!BaseDllInitialize+0x11F
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5

-----------------------------------------

DebugInfo          = 0x6A261F00
Critical section   = 0x77E1AEB8 (KERNEL32!BaseDllRegistryCache+0x18)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
Stack trace for DebugInfo = 0x6A261F00:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5

예외 오류 디버깅

예외 로그는 대상 프로세스에서 발생한 모든 예외를 기록합니다.

!avrf -ex Length 확장 명령을 사용하여 마지막 몇 가지 예외를 표시할 수 있습니다. Length는 예외 수를 지정합니다. Length를 생략하면 모든 예외가 표시됩니다.

예를 들면 다음과 같습니다.

0:000> !avrf -ex 4

=================================

Thread ID: 0000052c
Exception code: c0000008
Exception address: 6a226663
Exception record: 0012fb50
Context record: 0012fb64

Displayed 1 exception log entries.

디버깅에서 오류를 처리합니다.

!htrace는 사용자 모드 디버거와 커널 디버거 모두에서 사용하여 프로세스의 하나 또는 모든 핸들에 대한 스택 추적 정보를 표시할 수 있습니다. 이 정보는 프로세스에 대해 핸들 추적을 사용하도록 설정한 경우 사용할 수 있습니다. 애플리케이션 검증 도구에서 핸들 검사를 사용하도록 설정하면 자동으로 사용하도록 설정됩니다. 스택 추적은 프로세스가 핸들을 열거나 닫거나 잘못된 핸들을 참조할 때마다 저장됩니다. !htrace 확장에 대한 자세한 내용은 디버거 문서의 !htrace 를 참조하세요.

이 확장의 커널 디버거 구문은 다음과 같습니다.

!htrace [ handle [process] ]

핸들이 지정되지 않았거나 0이면 프로세스의 모든 핸들에 대한 정보가 표시됩니다. 프로세스를 지정하지 않으면 현재 프로세스가 사용됩니다.

사용자 모드 디버거 구문은 다음과 같습니다.

!htrace [handle]

사용자 모드 디버거 확장은 항상 현재 디버지 프로세스에 대한 정보를 표시합니다.

예:

프로세스 815328b0에서 핸들 7CC에 대한 덤프 정보

kd> !htrace 7CC 815328b0

Loaded \\...\kdexts extension DLL
Process 0x815328B0
ObjectTable 0xE15ECBB8

--------------------------------------

Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6

--------------------------------------

Handle 0x7CC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3180: ntoskrnl!ObpCreateHandle+0x304
0x801E1563: ntoskrnl!ObOpenObjectByName+0x1E9
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6

--------------------------------------

Parsed 0x1CA stack traces.
Dumped 0x2 stack traces.

프로세스 815328b0의 모든 핸들에 대한 덤프 정보

kd> !htrace 0 81400300

Process 0x81400300
ObjectTable 0xE10CCF60

--------------------------------------

Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7CC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE23B2: KERNEL32!CreateSemaphoreA+0x66
0x010011C5: badhandle!main+0x45
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - BAD REFERENCE:

0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - CLOSE:

0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Parsed 0x6 stack traces.

Dumped 0x5 stack traces.

현재 프로세스의 핸들 7DC에 대한 덤프 정보


kd> !htrace  7DC

Process 0x81400300

ObjectTable 0xE10CCF60

--------------------------------------

Handle 0x7DC - BAD REFERENCE:

0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - CLOSE:

0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Parsed 0x6 stack traces.

Dumped 0x3 stack traces.

힙 오류 디버깅

힙 검증 도구 디버거 확장

힙 검증 도구 디버거 확장은 !heap 확장(NT 힙 디버거 확장)의 일부입니다. !heap -?을 사용하여 간단한 도움말을 얻을 수 있습니다. !heap -p -? . 현재 확장은 프로세스에 대해 페이지 힙이 사용하도록 설정되어 있고 그에 따라 작동하는 경우 자체적으로 검색되지 않습니다. 지금은 확장의 사용자가 페이지 힙이 사용하도록 설정되어 있음을 알고 !heap -p 접두사 명령을 사용해야 합니다. !htrace 확장에 대한 자세한 내용은 디버거 문서의 !heap 을 참조하세요.

!heap -p

프로세스에서 만든 모든 전체 페이지 힙의 주소를 덤프합니다.

!heap -p -h ADDRESS-OF-HEAP

ADDRESS-OF-HEAP에서 전체 페이지 힙의 전체 덤프입니다.

!heap -p -a ADDRESS

ADDRESS에 힙 블록이 있는지 알아내려고 시도합니다. 이 값은 블록 시작의 주소일 필요는 없습니다. 이 명령은 메모리 영역의 특성에 대한 단서가 없는 경우에 유용합니다.

힙 작업 로그

힙 작업 로그는 모든 힙 루틴을 추적합니다. 여기에는 HeapAlloc, HeapReAlloc 및 HeapFree가 포함되며,

확장 명령을 사용하여 !avrf -hp Length 마지막 여러 레코드를 표시할 수 있습니다. Length는 레코드 수를 지정합니다.

를 사용하여 !avrf -hp -a Address 지정된 주소에 영향을 주는 모든 힙 공간 작업을 표시할 수 있습니다. 할당 작업의 경우 주소가 할당된 힙 블록에 포함되는 것으로 충분합니다. 무료 작업의 경우 블록 시작 부분의 정확한 주소를 지정해야 합니다.

로그의 각 항목에 대해 다음 정보가 표시됩니다.

  • 호출된 힙 함수입니다.
  • 루틴을 호출한 스레드의 스레드 ID입니다.
  • 호출과 관련된 주소입니다. 할당 루틴에서 반환되었거나 무료 루틴에 전달된 주소입니다.
  • 호출과 관련된 지역의 크기입니다.
  • 호출의 스택 추적입니다.

가장 최근 항목이 먼저 표시됩니다.

이 예제에서는 가장 최근의 두 항목이 표시됩니다.

0:001> !avrf -hp 2

alloc (tid: 0xFF4): 
address: 00ea2fd0 
size: 00001030
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00404ff3: Prymes!_stbuf+0xC3
00401c23: Prymes!printf+0x43
00401109: Prymes!main+0xC9
00402039: Prymes!mainCRTStartup+0xE9
77e7a278: kernel32!BaseProcessStart+0x23

alloc (tid: 0xFF4): 
address: 00ea07d0 
size: 00000830
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00403225: Prymes!_calloc_dbg+0x25
00401ad5: Prymes!__initstdio+0x45
00401f38: Prymes!_initterm+0x18
00401da1: Prymes!_cinit+0x21
00402014: Prymes!mainCRTStartup+0xC4

77e7a278: kernel32!BaseProcessStart+0x23

일반적인 디버그 시나리오

발생할 수 있는 몇 가지 오류 시나리오가 있습니다. 그들 중 일부는 전체 그림을 얻기 위해 탐정 작업의 꽤 많은 필요.

액세스할 수 없는 페이지의 액세스 위반

이는 테스트된 애플리케이션이 버퍼의 끝을 넘어 액세스하는 경우 전체 페이지 힙을 사용하도록 설정할 때 발생합니다. 해제된 블록에 닿으면 발생할 수도 있습니다. 예외가 발생한 주소의 특성을 이해하려면 다음을 사용해야 합니다.

!heap –p –a ADDRESS-OF-AV

손상된 블록 메시지

할당(할당, 사용자 무료, 실제 무료)의 수명 동안 몇 분 동안 페이지 힙 관리자는 블록에 모든 채우기 패턴이 그대로 있고 블록 헤더에 일관된 데이터가 있는지 확인합니다. 그렇지 않은 경우 검증 도구가 중지됩니다.

블록이 전체 페이지 힙 블록인 경우(예: 모든 할당에 대해 전체 페이지 힙이 사용하도록 설정되어 있는지 알고 있는 경우) "!heap –p –a ADDRESS"를 사용하여 블록의 특징을 확인할 수 있습니다.

블록이 라이트 페이지 힙 블록인 경우 블록 헤더의 시작 주소를 찾아야 합니다. 보고된 주소 아래에 30-40바이트를 덤프하여 시작 주소를 찾고 블록 헤더(ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA)에 대한 매직 시작/끝 패턴을 찾을 수 있습니다.

헤더는 오류를 이해하는 데 필요한 모든 정보를 제공합니다. 특히 매직 패턴은 블록이 할당되었는지 아니면 라이트 페이지 힙 또는 전체 페이지 힙 블록인지 여부를 알려줍니다. 여기에 있는 정보는 위반 전화와 신중하게 일치해야 합니다.

예를 들어 블록 주소와 4바이트를 더한 HeapFree를 호출하면 손상된 메시지가 표시됩니다. 블록 헤더는 잘 표시되지만 헤더 끝 뒤의 첫 번째 바이트(0xDCBAXXXX 매직 값 이후의 첫 번째 바이트)에는 다른 주소가 있는 다음 호출의 첫 번째 바이트가 있음을 확인해야 합니다.

특수 채우기 포인터

페이지 힙 관리자는 커널 포인터로 표시되는 값으로 사용자 할당을 채웁니다. 이는 블록이 해제되고(채우기 값이 F0임) 블록이 할당되지만 블록이 0이 되도록 요청하지 않을 때 발생합니다(채우기 값은 라이트 페이지 힙의 경우 E0, 전체 페이지 힙의 경우 C0). 0이 아닌 할당은 malloc/신규 사용자에게 일반적입니다. F0F0F0F0, E0E0E0E0, C0C0C0C0 같은 주소에서 읽기/쓰기가 시도되는 오류(액세스 위반)가 있는 경우 대부분 이러한 경우 중 하나에 도달했을 수 있습니다.

F0F0F0F0 읽기/쓰기는 블록이 해제된 후 사용되었음을 의미합니다. 불행하게도 당신은 이것을 일으킨 블록을 찾기 위해 몇 가지 탐정 작업이 필요합니다. 오류의 스택 추적을 가져와서 스택의 함수에 대한 코드를 검사해야 합니다. 그 중 하나는 할당이 활성 상태라는 잘못된 가정을 할 수 있습니다.

E0E0E0E0/C0C0C0C0 읽기/쓰기는 애플리케이션이 할당을 제대로 초기화하지 않았음을 의미합니다. 또한 현재 스택 추적의 함수에 대한 코드 검사가 필요합니다. 여기서는 이러한 종류의 실패에 대한 예입니다. 테스트 프로세스에서 주소 E0E0E0E0 HeapFree를 수행하는 동안 액세스 위반이 발견되었습니다. 테스트에서 구조를 할당하고 올바르게 초기화하지 않은 다음 개체의 소멸자를 호출한 것으로 밝혀졌습니다. 특정 필드는 null이 아니고(E0E0E0E0 있음) 삭제를 호출했습니다.

페이지 힙 기술 세부 정보

힙 손상(오버플로 또는 언더플로)을 감지하기 위해 AppVerifier는 요청된 메모리를 쓰기 불가능한 전체 페이지 또는 할당된 메모리 전후의 특수 태그로 패딩하여 메모리가 할당되는 방식을 수정합니다. AppVerifier는 확인 중인 프로세스에 Verifier.dll 로드하고 애플리케이션에서 호출한 Win32 힙 API 중 일부를 해당 Verifier.dll API로 리디렉션하여 이 작업을 수행합니다.

요청된 메모리를 쓰기 불가능한 전체 페이지로 패딩할 때(전체 설정은 페이지 힙 속성 섹션에서 사용하도록 설정되고 기본 설정임) AppVerifier는 많은 양의 가상 메모리를 사용하지만 오버플로 또는 언더플로가 발생할 때 힙 손상 이벤트가 실시간으로 캐시된다는 장점이 있습니다. 이 모드의 메모리는 [AppVerifier Read-Only 힙 페이지(4k)] [테스트 중인 애플리케이션에서 요청한 메모리 양] 또는 [테스트 중인 애플리케이션에서 요청한 메모리 양] [AppVerifier Read-Only 힙 페이지(4k)]와 같이 표시됩니다.

힙 검사 뒤로 속성에 따라 할당의 시작 또는 끝에 가드 페이지를 배치합니다. Backward이 기본값인 False로 설정된 경우 할당 끝에 가드 페이지를 배치하여 버퍼 오버런을 catch합니다. True로 설정하면 보호 페이지가 할당의 시작 부분에 배치되어 버퍼 언더런을 catch합니다.

요청된 메모리를 특수 태그로 패딩할 때(힙 속성에서 "전체" 검사 상자 항목을 지우면 사용됨) AppVerifier는 이 메모리가 해제될 때 검사 경고합니다. 이 기술을 사용할 때 기본 문제는 메모리가 해제될 때만 메모리 손상이 감지되는 경우가 있기 때문에(최소 메모리 블록 크기는 8바이트), 3바이트 변수 또는 5바이트 오버플로가 발생하면 즉시 검색되지 않는다는 것입니다.

언더플로 이벤트에서 Read-Only 페이지에 쓰려고 시도합니다. 그러면 예외가 트리거됩니다. 이 예외는 대상 애플리케이션이 디버거에서 실행되는 경우에만 catch할 수 있습니다. 전체 페이지 힙 모드는 패딩+가드 페이지를 사용하기 때문에 이러한 오류도 검색합니다. 라이트 페이지 힙을 사용하는 이유는 컴퓨터가 전체 페이지 힙의 높은 메모리 제약 조건을 허용할 수 없는 경우입니다.

메모리 집약적 애플리케이션의 경우 또는 오랜 기간 동안 AppVerifier를 사용해야 하는 경우(예: 스트레스 테스트) 성능 저하로 인해 전체 모드 대신 일반(조명) 힙 테스트를 실행하는 것이 좋습니다. 그러나 문제가 발생하면 전체 페이지 힙을 켜서 자세히 조사합니다.

사용자 지정 힙을 사용하는 애플리케이션(운영 체제의 힙 구현을 우회하는 힙)은 페이지 힙을 사용하는 모든 이점을 얻지 못하거나 사용하도록 설정할 때 오작동할 수도 있습니다.

메모리 오류 디버깅

메모리 검증 도구 디버거 확장

가상 공간 작업 로그는 프로세스의 가상 공간을 어떤 방식으로든 수정하는 모든 루틴을 추적합니다. 여기에는 VirtualAlloc, VirtualFree, MapViewOfFile 및 UnmapViewOfFile이 포함됩니다.

확장 명령을 사용하여 !avrf -vs Length 마지막 여러 레코드를 표시할 수 있습니다. Length는 레코드 수를 지정합니다.

!avrf -vs -a Address를 사용하여 지정된 주소에 영향을 주는 모든 가상 공간 작업을 표시할 수 있습니다. 할당의 경우 주소가 할당된 블록에 포함되는 것으로 충분합니다. 무료의 경우 지역 시작 부분의 정확한 주소를 지정해야 합니다.

로그의 각 항목에 대해 다음 정보가 표시됩니다.

  • 호출된 함수
  • 루틴을 호출한 스레드의 스레드 ID
  • 호출과 관련된 주소 - 할당 루틴에서 반환되었거나 무료 루틴에 전달된 주소입니다.
  • 호출에 관련된 지역의 크기
  • 메모리 작업 유형(AllocationType 매개 변수)
  • 요청된 보호 유형
  • 호출의 스택 추적

예제

가장 최근 항목이 먼저 표시됩니다.

다음 예제에서는 가장 최근의 두 항목이 표시됩니다.

0:001> !avrf -vs 2

VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef6525: mshtml+0x116525
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

VirtualFree (tid: 0xB4): addr:04bb0000 sz:00001000 op:4000 prot:0

        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef65ae: mshtml+0x1165AE
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

스레드가 먼저 페이지를 커밋 해제한 다음 전체 가상 지역을 해제한 0xB4 출력에서 확인할 수 있습니다.

주소 0x4BB1000 영향을 주는 모든 작업의 표시는 다음과 같습니다.

0:001> !avrf -vs -a 4bb1000

Searching in vspace log for address 04bb1000 ...

VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef6525: mshtml+0x116525
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

VirtualFree (tid: 0xB4): addr:04bb1000 sz:00001000 op:4000 prot:0

        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef65ae: mshtml+0x1165AE
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00010000 op:1000 prot:4

        00aa1ac2: verifier!VsLogCall+0x42
        00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
        68925ca3: kernel32!VirtualAllocEx+0x61
        68926105: kernel32!VirtualAlloc+0x16
        75ef63f3: mshtml+0x1163F3

VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00400000 op:2000 prot:4

        00aa1ac2: verifier!VsLogCall+0x42
        00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
        68925ca3: kernel32!VirtualAllocEx+0x61
        68926105: kernel32!VirtualAlloc+0x16
        75ef63d9: mshtml+0x1163D9

이 출력을 읽으려면 항목이 가장 최근의 항목부터 덤프됩니다. 따라서 이 로그는 스레드가 페이지를 커밋한 큰 지역을 할당할 0xB4 있음을 보여줍니다. 나중에 페이지를 커밋 해제한 다음 전체 가상 지역을 해제했습니다.

참고 항목

애플리케이션 검증 도구 - 개요

애플리케이션 검증 도구 - 애플리케이션 테스트

애플리케이션 검증 도구 - 애플리케이션 검증 도구 내의 테스트

애플리케이션 검증 도구 - 코드 및 정의 중지

애플리케이션 검증 도구 - 질문과 대답