'2013/05'에 해당되는 글 1건

  1. Application Verifier 사용법 2013.05.20

Application Verifier 사용법

from Develop/VC 2013. 5. 20. 12:16

글쓴 사람
정성태 (kevin13@chol.net)
홈페이지
첨부 파일
 
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++ 응용 프로그램 개발이 좀더 견고해지기를 바라겠습니다. ^^ (물론, 저 역시.)

,