시간 이동 디버깅 - 샘플 앱 연습
이 랩에서는 코드 결함이 있는 작은 샘플 프로그램을 사용하여 TTD(Time Travel Debugging)를 소개합니다. TTD는 문제를 디버그, 식별 및 근본 원인으로 지정하는 데 사용됩니다. 이 작은 프로그램의 문제는 쉽게 찾을 수 있지만 더 복잡한 코드에서 일반 프로시저를 사용할 수 있습니다. 이 일반 프로시저는 다음과 같이 요약할 수 있습니다.
- 실패한 프로그램의 시간 이동 추적을 캡처합니다.
- dx(디버거 개체 모델 식 표시) 명령을 사용하여 기록에 저장된 예외 이벤트를 찾습니다.
- !tt(시간 이동) 명령을 사용하여 추적에서 예외 이벤트의 위치로 이동합니다.
- 추적의 해당 지점에서 문제의 오류 코드가 범위에 들어올 때까지 한 단계 뒤로 이동합니다.
- 범위의 오류 코드를 사용하여 로컬 값을 확인하고 잘못된 값을 포함할 수 있는 변수의 가설을 개발합니다.
- 잘못된 값으로 변수의 메모리 주소를 확인합니다.
- ba(Access에서 중단) 명령을 사용하여 의심 변수의 주소에 대한 메모리 액세스(ba ) 중단점을 설정합니다.
- g-를 사용하여 의심 변수의 마지막 메모리 액세스 지점으로 다시 실행합니다.
- 해당 위치 또는 이전의 몇 가지 지침이 코드 결함의 포인트인지 확인합니다. 그렇다면 완료됩니다. 다른 변수에서 잘못된 값이 나온 경우 두 번째 변수의 액세스 중단점에 또 다른 중단을 설정합니다.
- g-를 사용하여 두 번째 의심 변수의 마지막 메모리 액세스 지점으로 다시 실행합니다. 해당 위치 또는 이전에 몇 가지 지침에 코드 결함이 포함되어 있는지 확인합니다. 그렇다면 완료됩니다.
- 오류를 발생시킨 잘못된 값을 설정하는 코드가 위치할 때까지 이 프로세스를 반복합니다.
이 절차에서 설명하는 일반적인 기술은 광범위한 코드 문제에 적용되지만 고유한 접근 방식이 필요한 고유한 코드 문제가 있습니다. 연습에 설명된 기술은 디버깅 도구 집합을 확장하는 데 사용되어야 하며 TTD 추적을 통해 가능한 몇 가지 방법을 보여 줍니다.
랩 목표
이 랩을 완료한 후에는 시간 이동 추적과 함께 일반 절차를 사용하여 코드에서 문제를 찾을 수 있습니다.
랩 설정
랩을 완료하려면 다음 하드웨어가 필요합니다.
- Windows 10 또는 Windows 11을 실행하는 랩톱 또는 데스크톱 컴퓨터(호스트)
랩을 완료하려면 다음 소프트웨어가 필요합니다.
- WinDbg입니다. WinDbg 설치에 대한 자세한 내용은 WinDbg - 설치를 참조하세요 .
- 샘플 C++ 코드를 빌드하는 Visual Studio입니다.
랩에는 다음 세 개의 섹션이 있습니다.
섹션 1: 샘플 코드 빌드
섹션 1에서는 Visual Studio를 사용하여 샘플 코드를 빌드합니다.
Visual Studio에서 샘플 앱 만들기
Microsoft Visual Studio에서 새>프로젝트/솔루션 파일>...을 클릭하고 Visual C++ 템플릿을 클릭합니다.
Win32 콘솔 애플리케이션을 선택합니다.
DisplayGreeting의 프로젝트 이름을 입력하고 확인을 클릭합니다.
SDL(보안 개발 수명 주기) 검사의 선택을 취소합니다.
마침을 클릭합니다.
다음 텍스트를 Visual Studio의 DisplayGreeting.cpp 창에 붙여넣습니다.
// DisplayGreeting.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <array> #include <stdio.h> #include <string.h> void GetCppConGreeting(wchar_t* buffer, size_t size) { wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!"; wcscpy_s(buffer, size, message); } int main() { std::array <wchar_t, 50> greeting{}; GetCppConGreeting(greeting.data(), sizeof(greeting)); wprintf(L"%ls\n", greeting.data()); return 0; }
Visual Studio에서 Project>DisplayGreeting 속성을 클릭합니다. 그런 다음 C/C++ 및 코드 생성을 클릭합니다.
다음과 같이 속성을 설정합니다.
설정 값 보안 확인 보안 검사 사용 안 함(/GS-) 기본 런타임 검사 기본값 참고 항목
이러한 설정은 권장되지 않지만 다른 사용자가 이러한 설정을 사용하여 코딩을 신속하게 수행하거나 특정 테스트 환경을 용이하게 하도록 조언하는 시나리오를 상상할 수 있습니다.
Visual Studio에서 빌드 솔루션 빌드>를 클릭합니다.
모든 것이 잘 진행되면 빌드 창에 빌드가 성공했음을 나타내는 메시지가 표시됩니다.
빌드된 샘플 앱 파일 찾기
솔루션 탐색기 DisplayGreeting 프로젝트를 마우스 오른쪽 단추로 클릭하고 파일 탐색기에서 폴더 열기를 선택합니다.
샘플에 대한 규격 exe 및 기호 pdb 파일이 포함된 디버그 폴더로 이동합니다. 예를 들어 프로젝트가 저장된 폴더인 경우 C:\Projects\DisplayGreeting\Debug로 이동합니다.
코드 결함으로 샘플 앱 실행
exe 파일을 두 번 클릭하여 샘플 앱을 실행합니다.
이 대화 상자가 나타나면 프로그램 닫기를 선택합니다 .
연습의 다음 섹션에서는 샘플 앱의 실행을 기록하여 이 예외가 발생하는 이유를 확인할 수 있는지 확인합니다.
섹션 2: "DisplayGreeting" 샘플의 추적 기록
섹션 2에서는 잘못된 동작 샘플 "DisplayGreeting" 앱의 추적을 기록합니다.
샘플 앱을 시작하고 TTD 추적을 기록하려면 다음 단계를 수행합니다. TTD 추적 기록에 대한 일반적인 내용은 시간 이동 디버깅 - 추적 기록 참조
시간 이동 추적을 기록할 수 있도록 관리자 권한으로 WinDbg를 실행합니다.
WinDbg에서 파일>시작 디버깅>실행 파일(고급)을 선택합니다.
기록하려는 사용자 모드 실행 파일의 경로를 입력하거나 찾아보기를 선택하여 실행 파일로 이동합니다. WinDbg에서 실행 메뉴 시작 작업에 대한 자세한 내용은 WinDbg - 사용자 모드 세션 시작을 참조 하세요.
실행 파일이 시작될 때 추적을 기록하려면 시간 이동 디버깅이 있는 레코드를 선택합니다.
구성 및 레코드를 클릭하여 녹음/녹화를 시작합니다.
"녹음/녹화 구성" 대화 상자가 나타나면 레코드를 클릭하여 실행 파일을 시작하고 녹음/녹화를 시작합니다.
추적이 기록되고 있음을 나타내는 기록 대화 상자가 나타납니다. 그 직후 애플리케이션이 충돌합니다.
프로그램 닫기를 클릭하여 "DisplayGreeting이 작동을 중지했습니다." 대화 상자를 닫습니다.
프로그램이 충돌하면 추적 파일이 닫히고 디스크에 기록됩니다.
디버거는 추적 파일을 자동으로 열고 인덱싱합니다. 인덱싱은 추적 파일의 효율적인 디버깅을 가능하게 하는 프로세스입니다. 이 인덱싱 프로세스는 더 큰 추적 파일에 더 오래 걸립니다.
(5120.2540): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: D:0 [Unindexed] Index !index Indexed 10/22 keyframes Indexed 20/22 keyframes Indexed 22/22 keyframes Successfully created the index in 755ms.
참고 항목
키 프레임은 인덱싱에 사용되는 추적의 위치입니다. 키 프레임은 자동으로 생성됩니다. 더 큰 추적에는 더 많은 키 프레임이 포함됩니다.
이 시점에서 추적 파일의 시작 부분에 있으며 시간이 지나면 앞뒤로 이동할 준비가 된 것입니다.
이제 TTD 추적을 기록했으므로 추적을 다시 재생하거나 추적 파일을 사용하여 작업할 수 있습니다(예: 동료와 공유). 추적 파일 작업에 대한 자세한 내용은 시간 이동 디버깅 - 추적 파일 작업 참조
이 랩의 다음 섹션에서는 추적 파일을 분석하여 코드와 관련된 문제를 찾습니다.
섹션 3: 추적 파일 기록을 분석하여 코드 문제 식별
섹션 3에서는 추적 파일 기록을 분석하여 코드 문제를 식별합니다.
WinDbg 환경 구성
다음 명령을 입력하여 기호 경로에 로컬 기호 위치를 추가하고 기호를 다시 로드합니다.
.sympath+ C:\MyProjects\DisplayGreeting\Debug .reload
다음 명령을 입력하여 소스 경로에 로컬 코드 위치를 추가합니다.
.srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
스택 및 지역 변수의 상태를 보려면 WinDbg 리본에서 보기 및 지역 및 보기 및 스택을 선택합니다. 창, 소스 코드 및 명령 창을 동시에 볼 수 있도록 창을 구성합니다.
WinDbg 리본에서 원본 및 오픈 소스 파일을 선택합니다. DisplayGreeting.cpp 파일을 찾아 엽니다.
예외 검사
추적 파일이 로드되면 예외가 발생한 정보가 표시됩니다.
2fa8.1fdc): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 15:0 eax=68ef8100 ebx=00000000 ecx=77a266ac edx=69614afc esi=6961137c edi=004da000 eip=77a266ac esp=0023f9b4 ebp=0023fc04 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 ntdll!LdrpInitializeProcess+0x1d1c: 77a266ac 83bdbcfeffff00 cmp dword ptr [ebp-144h],0 ss:002b:0023fac0=00000000
dx 명령을 사용하여 기록의 모든 이벤트를 나열합니다. 예외 이벤트가 이벤트에 나열됩니다.
0:000> dx -r1 @$curprocess.TTD.Events ... [0x2c] : Module Loaded at position: 9967:0 [0x2d] : Exception at 9BDC:0 [0x2e] : Thread terminated at 9C43:0 ...
참고 항목
이 연습에서는 불필요한 출력이 제거되었음을 나타내는 세 개의 마침표가 사용됩니다.
예외 이벤트를 클릭하여 해당 TTD 이벤트에 대한 정보를 표시합니다.
0:000> dx -r1 @$curprocess.TTD.Events[17] @$curprocess.TTD.Events[17] : Exception at 68:0 Type : Exception Position : 68:0 [Time Travel] Exception : Exception of type Hardware at PC: 0X540020
예외 필드를 클릭하여 예외 데이터를 자세히 드릴다운합니다.
0:000> dx -r1 @$curprocess.TTD.Events[17].Exception @$curprocess.TTD.Events[17].Exception : Exception of type Hardware at PC: 0X540020 Position : 68:0 [Time Travel] Type : Hardware ProgramCounter : 0x540020 Code : 0xc0000005 Flags : 0x0 RecordAddress : 0x0
예외 데이터는 CPU에서 throw된 하드웨어 오류임을 나타냅니다. 또한 액세스 위반임을 나타내는 0xc0000005 예외 코드를 제공합니다. 이는 일반적으로 액세스 권한이 없는 메모리에 쓰려고 했음을 나타냅니다.
예외 이벤트에서 [시간 이동] 링크를 클릭하여 추적에서 해당 위치로 이동합니다.
0:000> dx @$curprocess.TTD.Events[17].Exception.Position.SeekTo() Setting position: 68:0 @$curprocess.TTD.Events[17].Exception.Position.SeekTo() (16c8.1f28): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 68:0 eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046 eip=00540020 esp=00effe4c ebp=00520055 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 00540020 ??
이 출력에서는 스택과 기본 포인터가 매우 다른 두 주소를 가리키고 있다는 점에 유의하세요.
esp=00effe4c ebp=00520055
이는 스택 손상(함수가 반환된 다음 스택이 손상되었을 수 있음)을 나타낼 수 있습니다. 유효성을 검사하려면 CPU 상태가 손상되기 전으로 돌아가 스택 손상이 발생한 시기를 확인할 수 있는지 확인해야 합니다.
지역 변수 검사 및 코드 중단점 설정
추적 실패 시점에 오류 처리 코드의 실제 원인 이후 몇 단계를 완료하는 것이 일반적입니다. 시간 이동으로 우리는 진정한 근본 원인을 찾기 위해, 한 번에 지침을 다시 갈 수 있습니다.
홈 리본에서 한 단계씩 뒤로 물러나기 명령을 사용하여 세 가지 지침을 뒤로 밟습니다. 이렇게 하면 스택 및 메모리 창을 계속 검사합니다.
명령 창에는 세 가지 지침을 뒤로 물러나면서 시간 이동 위치와 레지스터가 표시됩니다.
0:000> t- Time Travel Position: 67:40 eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046 eip=00540020 esp=00effe4c ebp=00520055 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 00540020 ?? ??? 0:000> t- Time Travel Position: 67:3F eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046 eip=0019193d esp=00effe48 ebp=00520055 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 DisplayGreeting!main+0x4d: 0019193d c3 0:000> t- Time Travel Position: 67:39 eax=0000004c ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046 eip=00191935 esp=00effd94 ebp=00effe44 iopl=0 nv up ei pl nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212 DisplayGreeting!main+0x45:
참고 항목
이 연습에서 명령 출력은 명령줄 사용 기본 설정을 사용하는 사용자가 명령줄 명령을 사용할 수 있도록 UI 메뉴 옵션 대신 사용할 수 있는 명령을 보여 줍니다.
추적의 이 시점에서 스택 및 기본 포인터에는 더 적합한 값이 있으므로 손상이 발생한 코드의 지점에 가까워지는 것처럼 보입니다.
esp=00effd94 ebp=00effe44
또한 로컬 창에 대상 앱의 값이 포함되고 소스 코드 창이 추적의 이 시점에서 실행될 준비가 된 코드 줄을 강조 표시하고 있다는 점도 중요합니다.
자세히 조사하기 위해 메모리 창을 열어 0x00effe44 기본 포인터 메모리 주소 근처의 내용을 볼 수 있습니다.
연결된 ASCII 문자를 표시하려면 메모리 리본에서 텍스트를 선택한 다음 ASCII를 선택합니다.
명령을 가리키는 기본 포인터 대신 메시지 텍스트를 가리킵니다. 그래서 뭔가 바로 여기, 이것은 우리가 스택을 손상 한 시점에 가까운 수 있습니다. 추가 조사를 위해 중단점을 설정합니다.
참고 항목
이 아주 작은 샘플에서는 코드를 살펴보는 것이 매우 쉽지만 수백 줄의 코드와 수십 개의 서브루틴이 있는 경우 여기에 설명된 기술을 사용하여 문제를 찾는 데 필요한 시간을 줄일 수 있습니다.
TTD 및 중단점
중단점을 사용하는 것은 관심 있는 경우에 코드 실행을 일시 중지하는 일반적인 방법입니다. TTD를 사용하면 추적이 기록된 후 중단점이 적중될 때까지 중단점을 설정하고 시간을 거슬러 이동할 수 있습니다. 문제가 발생한 후 프로세스 상태를 검사하여 중단점에 가장 적합한 위치를 결정하는 기능을 통해 TTD에 고유한 추가 디버깅 워크플로를 사용할 수 있습니다.
메모리 액세스 중단점
메모리 위치에 액세스할 때 발생하는 중단점을 설정할 수 있습니다. 다음 구문과 함께 ba(액세스 중단) 명령을 사용합니다.
ba <access> <size> <address> {options}
옵션 | 설명 |
---|---|
e | execute(CPU가 주소에서 명령을 가져오는 경우) |
r | 읽기/쓰기(CPU가 주소에 읽거나 쓰는 경우) |
w | 쓰기(CPU가 주소에 쓰는 경우) |
지정된 시간에 4개의 데이터 중단점만 설정할 수 있으며 데이터를 올바르게 정렬하거나 중단점을 트리거하지 않도록 해야 합니다(단어는 2로 나눌 수 있는 주소로 끝나야 하고, dword는 4로 나눌 수 있어야 하고, 0 또는 8은 쿼드워드로 끝나야 함).
기본 포인터의 메모리 액세스 중단점에서 중단 설정
추적의 이 시점에서 기본 포인터인 ebp에 대한 쓰기 메모리 액세스에 대한 중단점을 설정하려고 합니다. 이 예제에서는 00effe44입니다. 이렇게 하려면 모니터링하려는 주소를 사용하여 ba 명령을 사용합니다. 4바이트에 대한 쓰기를 모니터링하려고 하므로 w4를 지정합니다.
0:000> ba w4 00effe44
보기를 선택한 다음 중단점을 선택하여 의도한 대로 설정되었는지 확인합니다.
홈 메뉴에서 [뒤로 이동]을 선택하여 중단점에 도달할 때까지 시간을 거슬러 이동합니다.
0:000> g- Breakpoint 0 hit Time Travel Position: 5B:92 eax=0000000f ebx=003db000 ecx=00000000 edx=00cc1a6c esi=00d41046 edi=0053fde8 eip=00d4174a esp=0053fcf8 ebp=0053fde8 iopl=0 nv up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 DisplayGreeting!DisplayGreeting+0x3a: 00d4174a c745e000000000 mov dword ptr [ebp-20h],0 ss:002b:0053fdc8=cccccccc
보기를 선택한 다음 로컬을 선택합니다. 로컬 창에서 대상 변수에 메시지의 일부만 있는 반면 원본에는 모든 텍스트가 포함되어 있음을 확인할 수 있습니다. 이 정보는 스택이 손상되었다는 생각을 지원합니다.
이 시점에서 프로그램 스택을 검사하여 활성 코드가 무엇인지 확인할 수 있습니다. 보기 리본에서 스택을 선택합니다.
Microsoft에서 제공한 wscpy_s() 함수에 다음과 같은 코드 버그가 있을 가능성은 매우 낮기 때문에 스택에서 자세히 살펴봅니다. 스택은 Greeting!main이 Greeting!을 호출하는 것을 보여줍니다. GetCppConGreeting. 아주 작은 코드 샘플에서는 이 시점에서 코드를 열고 오류를 매우 쉽게 찾을 수 있습니다. 그러나 더 크고 복잡한 프로그램에서 사용할 수 있는 기술을 설명하기 위해 추가 조사를 위한 새로운 중단점을 설정합니다.
GetCppConGreeting 함수에 대한 액세스 중단점에서 중단 설정
중단점 창을 사용하여 기존 중단점을 마우스 오른쪽 단추로 클릭하고 제거를 선택하여 기존 중단점을 지웁니다.
DisplayGreeting의 주소를 확인합니다. dx 명령을 사용하는 GetCppConGreeting 함수입니다.
0:000> dx &DisplayGreeting!GetCppConGreeting &DisplayGreeting!GetCppConGreeting : 0xb61720 [Type: void (__cdecl*)(wchar_t *,unsigned int)] [Type: void __cdecl(wchar_t *,unsigned int)]
ba 명령을 사용하여 메모리 액세스에 중단점을 설정합니다. 함수는 실행을 위해 메모리에서 읽기만 하므로 r - 읽기 중단점을 설정해야 합니다.
0:000> ba r4 b61720
중단점 창에서 하드웨어 읽기 중단점이 활성화되어 있는지 확인합니다.
인사말 문자열의 크기에 대해 궁금할 때 sizeof(greeting)의 값을 표시하도록 조사식 창을 설정합니다. 보기 리본에서 [조사식]을 선택하고 sizeof(greeting)를 제공합니다. 값이 범위에 없으면 조사식 창이 표시됩니다. 이름 'greeting'을 바인딩할 수 없습니다.
시간 이동 메뉴에서 시간 이동을 사용하여 추적을 시작 하거나 명령을 사용하여
!tt 0
추적의 시작으로 이동합니다.0:000> !tt 0 Setting position to the beginning of the trace Setting position: 15:0 (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 15:0 eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000 eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 ntdll!LdrpInitializeProcess+0x1d1c: 77a266ac 83bdbcfeffff00 cmp dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
홈 메뉴에서 Go를 선택하거나 명령을 사용하여
g
중단점에 도달할 때까지 코드에서 앞으로 이동합니다.0:000> g Breakpoint 2 hit Time Travel Position: 4B:1AD eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046 eip=00b61721 esp=00ddf7a4 ebp=00ddf864 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 DisplayGreeting!GetCppConGreeting+0x1: 00b61721 8bec mov ebp,esp
홈 메뉴에서 뒤로 나가기를 선택하거나 명령을 사용하여
g-u
한 단계를 뒤로 하십시오.0:000> g-u Time Travel Position: 4B:1AA eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046 eip=00b61917 esp=00ddf7ac ebp=00ddf864 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 DisplayGreeting!main+0x27: 00b61917 e8def7ffff call DisplayGreeting!ILT+245(?GetCppConGreetingYAXPA_WIZ) (00b610fa)
근본 원인을 찾은 것 같습니다. 선언한 인사말 배열의 길이는 50자이고 GetCppConGreeting에 전달하는 sizeof(greeting)는 0x64 100자입니다.
크기 문제를 자세히 살펴보면 메시지 길이가 75자이고 문자열 문자의 끝을 포함할 때는 76자임을 알 수 있습니다.
HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
코드를 수정하는 한 가지 방법은 문자 배열의 크기를 100으로 확장하는 것입니다.
std::array <wchar_t, 100> greeting{};
또한 이 코드 줄에서 sizeof(greeting)를 크기(인사말)로 변경해야 합니다.
GetCppConGreeting(greeting.data(), size(greeting));
이러한 수정 사항의 유효성을 검사하기 위해 코드를 다시 컴파일하고 오류 없이 실행되는지 확인할 수 있습니다.
원본 창을 사용하여 중단점 설정
이 조사를 수행하는 다른 방법은 코드 줄을 클릭하여 중단점을 설정하는 것입니다. 예를 들어 원본 창에서 std:array 정의 줄의 오른쪽을 클릭하면 중단점이 설정됩니다.
시간 이동 메뉴에서 시간 이동을 사용하여 추적의 시작으로 이동하는 시작 명령을 사용합니다.
0:000> !tt 0 Setting position to the beginning of the trace Setting position: 15:0 (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 15:0 eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000 eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 ntdll!LdrpInitializeProcess+0x1d1c: 77a266ac 83bdbcfeffff00 cmp dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
홈 리본에서 중단점이 적중될 때까지 이동하려면 [이동 ]을 클릭합니다.
Breakpoint 0 hit Time Travel Position: 5B:AF eax=0000000f ebx=00c20000 ecx=00000000 edx=00000000 esi=013a1046 edi=00effa60 eip=013a17c1 esp=00eff970 ebp=00effa60 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 DisplayGreeting!DisplayGreeting+0x41: 013a17c1 8bf4 mov esi,esp
인사말 변수의 액세스 중단점에서 중단 설정
이 조사를 수행하는 또 다른 방법은 의심 변수에 중단점을 설정하고 변경 중인 코드를 검사하는 것입니다. 예를 들어 GetCppConGreeting 메서드의 인사말 변수에 중단점을 설정하려면 이 절차를 사용합니다.
연습의 이 부분에서는 이전 섹션의 중단점에 여전히 있다고 가정합니다.
보기 및 로컬에서. 로컬 창 에서 현재 컨텍스트에서 인사말 을 사용할 수 있으므로 메모리 위치를 확인할 수 있습니다.
dx 명령을 사용하여 인사말 배열을 검사합니다.
0:000> dx &greeting &greeting : ddf800 : { size=50 } [Type: std::array<wchar_t,50> *] [<Raw View>] [Type: std::array<wchar_t,50>] [0] : 3 [Type: wchar_t] [1] : 0 [Type: wchar_t]
이 추적 에서 인사말 은 ddf800의 메모리에 있습니다.
중단점 창을 사용하여 기존 중단점을 마우스 오른쪽 단추로 클릭하고 제거를 선택하여 기존 중단점을 지웁니다.
쓰기 액세스를 모니터링하려는 메모리 주소를 사용하여 ba 명령으로 중단점을 설정합니다.
ba w4 ddf800
시간 이동 메뉴에서 시간 이동을 사용하여 추적의 시작으로 이동하는 시작 명령을 사용합니다.
0:000> !tt 0 Setting position to the beginning of the trace Setting position: 15:0 (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 15:0 eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000 eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 ntdll!LdrpInitializeProcess+0x1d1c: 77a266ac 83bdbcfeffff00 cmp dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
홈 메뉴에서 [이동]을 선택하여 인사말 배열의 첫 번째 메모리 액세스 지점으로 이동합니다.
0:000> g- Breakpoint 0 hit Time Travel Position: 5B:9C eax=cccccccc ebx=002b1000 ecx=00000000 edx=68d51a6c esi=013a1046 edi=001bf7d8 eip=013a1735 esp=001bf6b8 ebp=001bf7d8 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 DisplayGreeting!GetCppConGreeting+0x25: 013a1735 c745ec04000000 mov dword ptr [ebp-14h],4 ss:002b:001bf7c4=cccccccc
또는 추적의 끝으로 이동하고 코드를 통해 역방향으로 작업하여 배열 메모리 위치가 기록된 추적의 마지막 지점을 찾을 수 있습니다.
TTD를 사용합니다. 메모리 액세스를 볼 메모리 개체
추적 메모리에 액세스된 지점을 확인하는 또 다른 방법은 TTD를 사용하는 것입니다. 메모리 개체 및 dx 명령입니다.
dx 명령을 사용하여 인사말 배열을 검사합니다.
0:000> dx &greeting &greeting : 0xddf800 [Type: std::array<wchar_t,50> *] [+0x000] _Elems : "꽘棶檙瞝???" [Type: wchar_t [50]]
이 추적 에서 인사말 은 ddf800의 메모리에 있습니다.
dx 명령을 사용하여 읽기 쓰기 액세스 권한으로 해당 주소에서 시작하는 메모리의 4바이트를 확인합니다.
0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw") @$cursession.TTD.Memory(0x1bf7d0,0x1bf7d4, "rw") [0x0] [0x1] [0x2] [0x3] [0x4] [0x5] [0x6] [0x7] [0x8] [0x9] [0xa] ...
메모리 액세스 발생에 대한 자세한 정보를 표시하려면 발생 항목을 클릭합니다.
0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5] @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5] EventType : MemoryAccess ThreadId : 0x710 UniqueThreadId : 0x2 TimeStart : 27:3C1 [Time Travel] TimeEnd : 27:3C1 [Time Travel] AccessType : Write IP : 0x6900432f Address : 0xddf800 Size : 0x4 Value : 0xddf818 OverwrittenValue : 0x0 SystemTimeStart : Monday, November 18, 2024 23:01:43.400 SystemTimeEnd : Monday, November 18, 2024 23:01:43.400
TimeStart의 [시간 이동]을 클릭하여 추적을 특정 시점에 배치합니다.
0:000> dx @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo() @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo() (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 27:3C1 eax=00ddf81c ebx=00fa2000 ecx=00ddf818 edx=ffffffff esi=00000000 edi=00b61046 eip=6900432f esp=00ddf804 ebp=00ddf810 iopl=0 nv up ei pl nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212 ucrtbased!_register_onexit_function+0xf: 6900432f 51 push ecx
추적에서 마지막으로 발생하는 읽기/쓰기 메모리 액세스에 관심이 있는 경우 목록에서 마지막 항목을 클릭하거나 추가할 수 있습니다. dx 명령의 끝에 대한 Last() 함수입니다.
0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last() @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last() EventType : MemoryAccess ThreadId : 0x710 UniqueThreadId : 0x2 TimeStart : 53:100E [Time Travel] TimeEnd : 53:100E [Time Travel] AccessType : Read IP : 0x690338e4 Address : 0xddf802 Size : 0x2 Value : 0x45 SystemTimeStart : Monday, November 18, 2024 23:01:43.859 SystemTimeEnd : Monday, November 18, 2024 23:01:43.859
그런 다음 [시간 이동]을 클릭하여 추적에서 해당 위치로 이동하고 이 랩의 앞부분에서 설명한 기술을 사용하여 해당 시점에서 코드 실행을 자세히 살펴볼 수 있습니다.
TTD에 대한 자세한 내용을 보려면 메모리 개체는 TTD를 참조하세요 . Memory 개체입니다.
요약
이 아주 작은 샘플에서는 몇 줄의 코드를 확인하여 문제를 결정할 수 있었지만, 더 큰 프로그램에서는 여기에 제시된 기술을 사용하여 문제를 찾는 데 필요한 시간을 줄일 수 있습니다.
추적이 기록되면 추적 및 재현 단계를 공유할 수 있으며 모든 PC에서 문제를 재현할 수 있습니다.