Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 8. AppVerifier 사용법 [링크 복사], [링크+제목 복사],
조회: 30056
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
AppVerifier 사용법


.NET이 나온 이후로, 사실 VC++은 점차로 그 활용 영역이 줄고 있습니다. 사실, 줄고 있다는 표현보다는 그 영역이 좀 더 뚜렷해진다는 것이 더 옳을 것 같습니다. 엄밀히, VC++의 MFC를 이용해서 User Interface를 프로그래밍을 하는 것은 최악의 생산성이었다고 생각합니다. (물론, OLE Document Container 같은 분야에서는 MFC 도움 없이 만든다는 것은 엄청난 삽질이긴 하지만.)
실제로, 많은 부분에서 .NET의 WinForm으로 작성해도 무리가 없을 정도이고 오히려 그것이 작업 효율면에서 월등(조금이 아니라.)하게 낫습니다. 상황이 이렇게 되다보니, 어쩌면 MS의 VC++에 대한 지원이 소홀해 지는 것이 아닌가 하는 걱정이 있을 수도 있는데요. 오히려 MS는 VS.NET 2005와 함께 VC++에 대한 변함없는 지원을 계속하고 있습니다.

Code Coverage 기능도 Native 코드까지 가능하도록 확장했고, 정적 코드 분석(static code analysis) 및 AppVerifier까지 기능이 더욱 보강이 되어서 나왔습니다. ^^

그동안 나름대로 제 프로젝트 자체에도 VC++ 8.0 + VS.NET 2005 IDE를 사용하면서, 예전보다는 응용 프로그램에 대한 신뢰도가 많이 올라가는 것을 알 수 있습니다.
제가 VC++ 프로젝트에 적용하는 것은 다음과 같습니다.

  • 경고 레벨 4 (/W4)
  • 경고를 오류로 취급 (/WX)
  • Base Address 지정
  • Enable Code Analysis For C/C++ (/analyze)
  • Secured CRT Library

물론, 기존 프로젝트에 대해서는 점진적으로 적용하는 항목도 있지만 적어도 새로운 프로젝트를 시작할 때는 반드시 적용시키고 있습니다.
거기에 더해서, 최근에는 Code Annotations 및 AppVerifier를 통한 검증 단계까지 추가하고 있습니다.

하다 보니 꽤 흥미롭습니다. 만약, 제가 S/W를 발주해야 할 지위에 있다면 VC++ 프로젝트에 한해서만큼은 위의 요구 사항을 만족시켜야 한다고 요구하겠습니다.
비록 위의 조건을 만족했다고 해서 응용 프로그램을 100% 신뢰할 수 있다고는 할 수 없겠지만, 그래도 프로그래머가 할 수 있는 실수에 대해 최소화 시킬 수 있는 장치를 통과했다고 할 수 있다고는 판단할 수 있으니까요.

본격적으로 AppVerifier에 대한 사용법을 살펴보기 전에 간단한 참조 URL 먼저 소개해 보겠습니다.

다운로드
; http://www.microsoft.com/downloads/details.aspx?familyid=BD02C19C-1250-433C-8C1B-2619BD93B3A2&displaylang=en

Using Application Verifier Within Your Software Development Lifecycle
; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/appverifier_sdl.asp

현재(2006.08.19 기준) 3.2.0038 버전까지 나왔습니다. 이전 버전을 가지고 계신 분은 이번 기회에 다시 다운로드해서 설치하십시오.



그럼, 간략하게 사용법을 볼까요? 어차피 "Using Application Verifier Within Your Software Development Lifecycle" 문서의 일부분을 한글로 번역한 것과 마찬가지이겠지만 그래도 부담감을 덜기 위해서 중복 설명을 감수하고 써보겠습니다. ^^

우선, AppVerifier를 설치하게 되면 "C:\Program Files\Application Verifier 3.2" 폴더가 생성되고 그 하위에는 AppVerifier에 응용 프로그램에 대한 "바로가기"가 놓여 있습니다. 실제 응용 프로그램은 "AppVerif.exe"로 system32 폴더에 복사되어 있기 때문에 "시작" / "실행" 메뉴를 통해서도 쉽게 접근할 수 있습니다.
순서대로 설명을 해보면,

1. 실행 화면은 다음과 같습니다.

AppVerifier 실행 화면

2. 테스트가 제대로 되는지 확인하기 위해서 일부러 결함이 있는 코드로 응용 프로그램을 만들어 보겠습니다.

01: #include "stdafx.h"
02: 
03: int _tmain(int argc, _TCHAR* argv[])
04: {
05:		HANDLE hEvent = ::CreateEvent( NULL, TRUE, TRUE, L"Test" );
06:		CloseHandle( hEvent );
07: 
08:		SetEvent( hEvent ); /* 오류 */ 
09: 
10:		return 0;
11: }

코드를 위해 단순화 시켰지만, 실제로 복잡한 멀티 스레딩 응용 프로그램에서는 가능한 오류일 텐데요. 보시는 것처럼, 오류는 8번째 줄에 있으며 이러한 오류는 Code Analysis, Code Annotations, 경고 레벨 4와 같은 장치로는 걸리지 않습니다. 다행히 AppVerifier는 이러한 오류를 잡아냅니다.

3. 컴파일합니다. 이 응용 프로그램의 이름은 ConsoleApp.exe입니다.

4. Ctrl + A 키를 누르면, 다음과 같이 응용 프로그램을 추가할 수 있습니다. 사실, 응용 프로그램의 경로는 중요하지 않습니다. 따라서 debug 폴더에 있는 것을 선택하나 release 폴더에 있는 것을 선택하나 별반 다르지 않습니다. 중요한 것은 해당 응용 프로그램 이름과 똑같은 경우에 무조건 검증 작업이 들어간다는 점입니다. 응용 프로그램은 몇 개를 추가해도 상관없고, 검증하고자 하는 모듈이 DLL인 경우에는 해당 모듈을 실행시켜주는 프로세스를 선택하면 됩니다. 예를 들어, ActiveX 컨트롤이라면 iexplore.exe를 선택해야 합니다.

검증할 응용 프로그램 추가

5. 선택하고 나면, 아래 화면과 같이 Image Name에 대해서 검증하게 될 항목들이 오른쪽에 선택할 수 있도록 해줍니다. 여기서는 그냥 Basics에 선택된 기본값만을 가지고 진행해 보겠습니다. 선택된 후에는 반드시 "Save" 버튼을 눌러야만 설정사항이 반영됩니다. 만약 설정만 하고 Save 버튼을 누르지 않은 체로 응용 프로그램을 실행하면 아무런 검증도 받을 수 없습니다. 마찬가지로, 해당 응용 프로그램에 대한 검증을 더 이상 하고 싶지 않아서 삭제한 경우에도 "Save" 버튼을 누른 이후에만 반영이 됩니다.

선택된 응용 프로그램

6. 이제, ConsoleApp.exe를 실행하면 다음과 같은 예외가 발생합니다. AppVerifier가 없었다면 발생하지 않을 예외입니다.
일단은 문제 해결보다는 AppVerifier 사용법에 집중하기 위해, 아래의 화면에서 "No"를 선택해서 응용 프로그램을 종료시킵니다.

예외 발생

7. AppVerifier에서, Ctrl + L 키를 누르면, 다음과 같은 로그를 볼 수 있는 창이 뜹니다.

예외 로그

8. 보시는 것처럼 "Error"가 1개 있는데, 해당 로그를 선택하고 우측의 "View" 버튼을 누르면 다음과 같이 오류 내용을 Internet Explorer에서 볼 수 있습니다. 가끔 오류와 관련해서 "<, >, &" 문자 등이 포함되어 있는 경우, (현재 버전의) AppVerifier에 버그가 있어서 "&lt, &gt, &amp"와 같이 끝에 ";" 문자를 빼먹어서 XML을 Internet Explorer에 보는 것이 실패하는 경우가 있습니다. 그런 경우에는 Internet Explorer에서 XML 파일로 저장한 후 그 부분의 치환을 변경해 주시던지, 그 부분을 감안하고 직접 메모장에서 열어서 보는 것도 방법입니다. 참고로, 실제 로그 파일의 위치는 "C:\Documents and Settings\[로그인 사용자]\AppVerifierLogs"이고 확장자가 dat인 이진 파일로 구성되어 있기 때문에 별로 도움은 되지 않습니다.

01: <?xml version="1.0" encoding="UTF-8" standalone="no"?>
02: <avrf:logfile xmlns:avrf="Application Verifier">
03:		<avrf:logSession TimeStarted="2006-08-19 : 14:26:07" PID="480" Version="1">
04:			<avrf:logEntry Time="2006-08-19 : 14:26:07" LayerName="Handles" StopCode="0x300" Severity="Error">
05:				<avrf:message>Invalid handle exception for current stack trace.</avrf:message>
06:				<avrf:parameter1>c0000008 - Exception code.</avrf:parameter1>
07:				<avrf:parameter2>12fe38 - Exception record. Use .exr to display it.</avrf:parameter2>
08:				<avrf:parameter3>12fb5c - Context record. Use .cxr to display it.</avrf:parameter3>
09:				<avrf:parameter4>0 - Not used.</avrf:parameter4>
10:				<avrf:stackTrace>
11:					<avrf:trace>vfbasics!VfBasicsStopMessage+d1</avrf:trace>
12:					<avrf:trace>vfbasics!AVrfpVectoredExceptionHandler+9b</avrf:trace>
13:					<avrf:trace>ntdll!RtlpCallVectoredHandlers+57</avrf:trace>
14:					<avrf:trace>ntdll!RtlCallVectoredExceptionHandlers+15</avrf:trace>
15:					<avrf:trace>ntdll!RtlDispatchException+19</avrf:trace>
16:					<avrf:trace>ntdll!RtlRaiseException+3d</avrf:trace>
17:					<avrf:trace>ntdll!KiRaiseUserExceptionDispatcher+37</avrf:trace>
18:					<avrf:trace>ntdll!KiFastSystemCallRet+0</avrf:trace>
19:					<avrf:trace>vfbasics!AVrfpHandleSanityChecks+51</avrf:trace>
20:					<avrf:trace>vfbasics!AVrfpNtSetEvent+23</avrf:trace>
21:					<avrf:trace>kernel32!SetEvent+10</avrf:trace>
22:					<avrf:trace>ConsoleApp!wmain+22</avrf:trace>
23:					<avrf:trace>kernel32!BaseProcessStart+23</avrf:trace>
24:				</avrf:stackTrace>
25:			</avrf:logEntry>
26:		</avrf:logSession>
27: </avrf:logfile>


9. 오류 로그 내용은 제법 친절합니다. 22라인을 보면, wmain이 우리의 마지막 모듈이고, kernel32의 SetEvent를 호출했을 때 vfbasics 모듈들에서 가로채기 당해서 예외가 난 것을 볼 수 있습니다. 다행스러운 것은 위의 로그 내용이 debug / release에 상관없이 그처럼 자세히 나온다는 것입니다.



이제까지 사용법을 대강 살펴 보았습니다. 그리 어렵지 않지요? ^^ 사실 몰라서 못 쓴 것일뿐 그리 어려운 주제는 아닙니다. 이 정도면 훌륭한 툴 아닐까요!

AppVerifier의 강력함은 사실, 위의 로그 파일에 있는 것이 아닙니다. 오히려, 의심이 되는 바로 그 지점에서 int 3의 예외를 발생시킨다는 것이 더욱 큰 기능입니다. 이름에서 알 수 있듯이 이것은 Verifier이지 디버깅 도구가 아닙니다. 물론, 어떤 알 수 없는 버그가 발생했을 때 그것을 유발시킨 결함이 AppVerifier에 의해서 잡힌 지점과 연관이 있을 수도 있지만 중요한 것은 사용자 컴퓨터에 내려가서 실행되는 성질의 것은 아니라는 것입니다. 그러니, 이 도구는 제품을 출시하기 전에 - 바람직하게는 Application을 개발하는 도중에 틈틈이 - 전체적으로 Application에 대한 결함이 없는지를 검증하는 목적으로 사용될 수 있는 것입니다. 따라서, PDB 파일과 소스 파일이 완벽하게 갖춰진 상태에서 검증 단계를 밟을 수 있기 때문에 위와 같은 다소 어려운 로그를 보면서 디버깅을 하지 않아도 됩니다.
그럼, 어떻게 하는 지 볼까요? ^^



1. 다시 프로그램을 실행시켜서, 예외가 발생하는 창이 뜨면 "New instances of Visual Studio 2005"를 선택하면 다음과 같은 디버깅 화면이 나옵니다.

디버깅 진입

2. "Break" 버튼을 누르고, "Call Stack" 윈도우를 확인하면 아래와 같습니다.

	ntdll.dll!7c822583()	
	[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]	
	vrfcore.dll!VerifierStopMessageEx(_AVRF_LAYER_DESCRIPTOR * LayerDescriptor=0x003c6ae0, unsigned long StopCode=0x00000300, unsigned long Param1=0xc0000008, unsigned long Param2=0x0012fd4c, unsigned long Param3=0x0012fa70, unsigned long Param4=0x00000000, _AVRF_STOP_EXTRA * StopExtra=0x00000000, ...)  Line 523	C++
	vfbasics.dll!VfBasicsStopMessage(unsigned long Code=0x00000300, char * Message=0x003b3044, unsigned long Param1=0xc0000008, char * Description1=0x003b21ac, unsigned long Param2=0x0012fd4c, char * Description2=0x003b2218, unsigned long Param3=0x0012fa70, char * Description3=0x003b2244, unsigned long Param4=0x00000000, char * Description4=0x003b21bc)  Line 1053 + 0x20 bytes	C
	vfbasics.dll!AVrfpVectoredExceptionHandler(_EXCEPTION_POINTERS * ExceptionPointers=0x0012f9cc)  Line 243	C
	ntdll.dll!7c84f937()	
;
[중간생략]
;	
	ntdll.dll!7c82ed3b()	
>	msvcr80d.dll!_free_dbg(void * pUserData=0x02391020, int nBlockUse=0x02392010)  Line 1194 + 0xd bytes	C++
	ntdll.dll!7c868354()	
;
[중간생략]
;
	ntdll.dll!7c821a34()	
	vfbasics.dll!AVrfpCheckObjectType(void * Handle=0x000001d8, AVRFP_OBJECT_TYPE_INDEX ObjectTypeIndex=AVrfpEventTypeIndex)  Line 81 + 0x1c bytes	C
	vfbasics.dll!AVrfpHandleSanityChecks(void * Handle=0x000001d8, AVRFP_OBJECT_TYPE_INDEX ObjectTypeIndex=AVrfpEventTypeIndex)  Line 128	C
	vfbasics.dll!AVrfpNtSetEvent(void * EventHandle=0x000001d8, long * PreviousState=0x00000000)  Line 270	C
	kernel32.dll!77e670ea()		
	ConsoleApp.exe!wmain(int argc=0x00000001, wchar_t * * argv=0x02404f10)  Line 12 + 0xc bytes	C++
	ConsoleApp.exe!__tmainCRTStartup()  Line 583 + 0x19 bytes	C
	ConsoleApp.exe!wmainCRTStartup()  Line 403	C
	kernel32.dll!77e523e5()		

3. 호출 스택의 아랫 부분을 보면 "ConsoleApp.exe!wmain" 부분이 있습니다. 그 라인을 더블 클릭하면, 해당 소스에서 오류가 발생한 라인으로 다음과 같이 보여줍니다. 그렇군요. 바로 그 라인에 결함이 발생한 것입니다. 디버거답게 모든 지역 변수 및 전역 변수의 값도 볼 수 있습니다. 이 정도면 웬만한 결함은 손쉽게 수정이 가능하겠지요. ^^

결함이 있는 소스 코드



그러고 보면, 이러한 소스 코드 라인까지의 디버깅 및 온전한 호출 스택을 얻기 위해서는 PDB 파일의 도움이 필요하군요. 역시나 PDB 파일의 중요성이 새삼 다가오는 순간입니다.

이제부턴, AppVerifier를 십분 활용하셔서, 여러분들의 VC++ 응용 프로그램 개발이 좀 더 견고해지기를 바라겠습니다. ^^ (물론, 저 역시.)




[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]
[연관 글]






[최초 등록일: ]
[최종 수정일: 7/17/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2006-09-07 03시02분
Catch C/C++ Errors Early with VSTS Code Verifier
; http://www.codeguru.com/columns/kate/article.php/c12549/
kevin25

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13610정성태4/28/2024205닷넷: 2251. C# - 제네릭 인자를 가진 타입을 생성하는 방법 - 두 번째 이야기
13609정성태4/27/2024232닷넷: 2250. PInvoke 호출 시 참조 타입(class)을 마샬링하는 [IN], [OUT] 특성파일 다운로드1
13608정성태4/26/2024426닷넷: 2249. C# - 부모의 필드/프로퍼티에 대해 서로 다른 자식 클래스 간에 Reflection 접근이 동작할까요?파일 다운로드1
13607정성태4/25/2024471닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/2024627닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/2024774닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/2024814오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/2024949닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/2024962닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/2024990닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/20241014닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024949닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024993닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드2
13597정성태4/15/2024987닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/20241101닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/20241071닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241093닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241094닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241230C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241207닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241086Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241164닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241535닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신 [2]파일 다운로드1
13587정성태3/27/20241396오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241605Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...