DCL Runtime Debugging Support 2005.06.08 김대중 <daejung@sysdeveloper.net> http://www.sysdeveloper.net/daejung 요약 버그없는소프트웨어를개발하는것은결코쉬운일이아니다. 대부분의상업적개발도구들이소스프로그램을추적할수있도록하는디버깅환경을제공하고있고이러한것들은단위함수들을디버깅하는데있어서매우유용한건사실이다. 그러나, 시스템전반에걸친문맥 (context) 오류를발견하기위해서는별로도움이되지못한다. 특히, 메모리누출과같은버그들은기존의소프트웨어개발도구를사용하여발견해내는것은거의불가능하다고할수있다. 따라서, 이러한것들을위해서는프로그램작성단계에서부터버그의원인을알아내도록하는대책이필요하다. 이러한사유로 DCL은소스코드차원에서의디버깅을위한코드삽입을자동화하도록하여소프트웨어의개발단계및테스트단계에서필요한정보를생성하도록하는런타임환경을제공하고있다. 특히, 동적메모리관련디버깅환경부분은프로세스뿐만아니라다중스레드어플리케이션에서각각의스레드별추적도가능하도록하고있다. 본문서는런타임디버깅환경을위한 DCL 의 ASSERT, TRACE, 그리고동적메모리할당과관련한 참조매뉴얼을제공한다. - 1 -
- 목차 1. 디폴트컴파일러매크로...3 1.1 _DEBUG...3 1.2 NDEBUG...3 2. 디버그버전라이브러리컴파일매크로...3 2.1 DCL_DEBUG...3 2.2 DCL_HAVE_ALLOC_DEBUG...3 2.3 DCL_HAVE_STRING_ALLOC_DEBUG...3 2.4 DCL_HAVE_THROW_LOCATION...3 2.5 THIS_FILE...4 3. 디버그라이브러리의메시지출력...4 3.1 DCLInitialize...4 3.2 DCLCleanup...4 3.3 DCLDebugSetGlobalReport...4 3.4 DCLDebugSetThreadReport...5 3.5 OutputStream...5 4. ASSERTION...5 4.1 ASSERT...5 4.2 VERIFY...6 4.3 DCLAssert...6 4.4 DCLAssertSetAction...6 5. TRACE...6 5.1 TRACE의종류...7 5.2 DCLTrace...7 6. 동적메모리디버그...8 6.1 Functions and Operators...8 6.2 DCLDebugGetLastAllocPosition...8 6.3 DCLDebugDumpThreadMemoryLeak...8 6.4 DCLDebugDumpGlobalMemoryLeak...9 7. 예외 (exception) 위치보고...9 7.1 DCL_THROW...9-2 -
1. 디폴트컴파일러매크로 1.1 _DEBUG 이매크로는 Microsoft Visual C++ 에서디버그버전으로컴파일할때사용된다. DCL 에서이매크 로가설정하려면 CRTD Debug 설정을사용하여 MSVCRTD.DLL 을사용하도록하여야한다. 이것의 사용목적은 DCL 자체의디버깅을위해사용되어진다. DCL 을사용하여프로그램을작성하는대부분의경우에는이매크로사용하지않도록하여야한다. 1.2 NDEBUG 표준 C 의런타임라이브러리인 LIBC 에서사용하는매크로로표준 assert 와관련이있다. LIBC 에서 제공하는 assert 의기본동작은 abort 하기때문에 DCL 을사용하여개발할때에는이것을사용하지않 도록하여야한다. 컴파일타임에 NDEBUG 가정의되면표준 assert 매크로는컴파일타임에무시된다. 2. 디버그버전라이브러리컴파일매크로 DCL 에포함된디버깅코드는원칙적으로 C/C++ 의매크로기능을사용한다. 따라서릴리즈버전으로 컴파일이된코드에는대부분의디버깅코드가포함되지않는다. 디버그버전라이브러리를위한매크 로의기본설정은 dcl/_config.h 에포함되어있다. 2.1 DCL_DEBUG 라이브러리를디버깅버전으로컴파일하기위해서는 dcl/_config.h 에포함된 DCL_DEBUG 가정의 (define) 되어야한다. 2.2 DCL_HAVE_ALLOC_DEBUG 동적메모리할당관련디버깅을활성화한다. 이매크로가정의되면 new, new[], delete, delete[], malloc, calloc, realloc, free 에대하여메모리할당과사용에대하여런타임디버깅정보를유지한다. 2.3 DCL_HAVE_STRING_ALLOC_DEBUG DCL 의 String 클래스는문자열을저장하기위하여 new[] 연산자를사용한동적인메모리할당을한 다. 이것은 String 클래스의버그를발견하기위함으로디폴트설정은비활성화되어있다. 2.4 DCL_HAVE_THROW_LOCATION 예외가발생하여 Exception 객체를던질 (throw) 때예외가발생한소스파일및라인정보를포함하 도록한다. DCL_DEBUG 가정의되어있으면이것의디폴트설정은활성화된다. - 3 -
2.5 THIS_FILE TRACE, ASSERT, DCL_THROW, malloc, calloc, realoc, free, new, new[] 등의위치를위한소스 파일의이름을지정한다. 이것을지정하지않으면기본적으로 FILE 일매크로로대체된다. 3. 디버그라이브러리의메시지출력 3.1 DCLInitialize DCLCAPI bool DCLInitialize( void*, // reserved must NULL int nfd // for Debug Report -1(no debug) or valid file descriptor DCL 을사용하기위하여반드시이함수를호출하여야한다. 디버그버전에서 nfd 가 1 이상의유 효한파일기술자 (descriptor) 이면, 내부적으로 XFileOutputStream(Mutexed FileOutputStream) 객체를 생성하여그포인터를사용하여 DCLDebugSetGlobalReport 를호출한다. DCLInitialize 는내부적으로함수호출카운터를유지하기때문에 2 번이상의호출에는아무런행위를 하지않고 true 를리턴한다. 3.2 DCLCleanup DCLCAPI void DCLCleanup( DCL 을사용하면내부적으로시스템자원을할당한다. 이함수는 DCL 이사용한시스템자원을해제 한다. 디버그버전에서는 DCLInitialize 에서할당한 XFileOutputStream 이사용한자원을해제한다. 라이브러리가 DCL_HAVE_ALLOC_DEBUG 가활성화있고 DCLDebugSetGlobalReport 에의하여 설정된스트림이있으면 DCLDebugDumpGlobalMemoryLeak 을호출하여메모리누출을보고한다. 3.3 DCLDebugSetGlobalReport DCLCAPI DCL_NAMESPACE OutputStream* DCLDebugSetGlobalReport( DCL_NAMESPACE OutputStream* pnewoutput 프로세스전역에걸쳐 DCL_TRACE, DCL_ASSERT의출력스트림을설정한다. 리턴값은이전의스트림객체의포인터로 NULL 일수있다. 다중스레드어플리케이션의경우 OutputStream의 write 멤버는뮤텍스에의해동기되어져있어야한다. - 4 -
3.4 DCLDebugSetThreadReport DCLCAPI DCL_NAMESPACE OutputStream* DCLDebugSetThreadReport( unsigned long uthreadid, DCL_NAMESPACE OutputStream* pnewoutput 스레드 uthreadid의디버깅출력을설정한다. 이함수를사용하여특정스레드에디버깅출력이설정되면 DCL_TRACE, DCL_ASSERT는이출력스트림에메시지를출력한다. 스레드를위한 OutputStream 객체는특정스레드에의해서만사용되기때문에뮤텍스 (mutex) 에의한동기화가필요없다. 3.5 OutputStream 디버깅출력은스트림객체의 write, print, printf 등의멤버함수를통해이루어진다. 다중스레드어 플리케이션의경우에는뮤텍스에의해출력이동기화될필요가있다. OutputStream 클래스의다중스레드안전버전은 X 접두사를갖는다. 대표적인클래스는 XFileOutputStream, XByteBufferOutputStream 과같은것이있다. 4. ASSERTION ASSERTION 는해당위치에서주어진조건을만족하지못하면조건식, 소스파일, 라인을출력하고프 로그램의실행을중단 (abort) 하는강력한디버깅도구이다. DCL 에삽입된 ASSERTION 의동작은 C- 런타임의 assert 와동일한동작외에 AssertException* 를 throw 하게할수있다. 이는데몬프로세스나 CGI 프로그램과같이프로그램의실행이사용자와직접 적인상호작용이불가능할경우유용하다. 4.1 ASSERT DCL_ASSERT(expr), ASSERT(expr) DCL_ASSERT_EX(expr, msg), ASSERT(expr, msg) expr 이실패 (false) 하면 ASSERTION 동작을한다. DCL_DEBUG 가정의되어있을때에만의미가있 으며릴리즈버전에서 expr 은그문장자체가무시된다. DCL_ASSERT_EX 는 expr 과함께 msg 를출 력한다. ASSERT 는 DCL_ASSERT 와동일하다. - 5 -
4.2 VERIFY DCL_VERIFY(expr), VERIFY(expr) ASSERT 와동일한동작을하며 DCL_DEBUG 가정의되어있지않으면 expr 은평가되지않고그대 로실행된다. ASSERT 와 VERIFY 의차이점은릴리즈버전에서 VERIFY 의 expr 은정상적인문장의역할을수행하 고 ASSERT 의 expr 은제거된다는것이다. VERIFY 는 DCL_VERIFY 와동일하다. 4.3 DCLAssert DCLCAPI void DCLAssert( const char* pszexpr, const char* pszmessage, const char* pszfilename, int nline ) DCL_THROW; DCL_ASSERT(expr) 와 DCL_ASSERT_EX(expr, msg) 가실패하면호출되는함수이다. 입력 expr 과 msg 의내용을출력하고 abort 하거나 throw 한다. 4.4 DCLAssertSetAction DCLAssertAction DCLAssertSetAction(DCLAssertAction assertaction) DCLAssert 의동작을결정한다. assertaction 은 DCL_ASSERT_ABORT 와 DCL_ASSERT_THROW 이 어야하며이함수를호출하기전의디폴트값은 DCL_ASSERT_ABORT 이다. 함수호출후리턴값은 이전에설정된값이다. DCL_ASSERT_ABORT 는 DCL_ASSERT(expr) 의 expr 을출력하고 abort 되며, DCL_ASSERT_THROW 는 AssertException* 를 throw 한다. 5. TRACE TRACE 는프로그램의실행중에유용한정보를출력할수있도록하는매크로이다. TRACE 는디버 그버전에서만컴파일된다. TRACE 의출력은프로세스에할당된파일과쓰레드에할당된파일이있을수있으며, 쓰레드에할당 된파일이있으면이곳에기록을하고그렇지않으면프로세스에할당된파일에기록한다. 어떠한파일 - 6 -
도할당되어있지않으면 TRACE 의출력은무시된다. 5.1 TRACE 의종류 DCL_TRACE0(psz) DCL_TRACE1(fmt, arg1) DCL_TRACE2(fmt, arg1, arg2) DCL_TRACE3(fmt, arg1, arg2, arg3) DCL_TRACE4(fmt, arg1, arg2, arg3, arg4) TRACE0(psz) TRACE1(fmt, arg1) TRACE2(fmt, arg1, arg2) TRACE3(fmt, arg1, arg2, arg3) TRACE4(fmt, arg1, arg2, arg3, arg4) DCL_TRACE 의사용은 printf 함수를사용하는것과동일하다. fmt 는반드시상수문자열이어야한다. const char* pszformat = %d ; TRACE1(pszFormat, 10 과같은형태로사용할수없으며 TRACE1( %d n, 10 과같이사용하여야한다. 이는 TRACE 매크로가출력포맷을변경하여소스파일의파일이름과라인 번호를추가하기때문이다. TRACE 는 DCL_TRACE 와동일하다. 라이브러리내부에서는 DCL_TRACE 가사용되고있으며이 것은미래의다른라이브러리와의충돌을방지하기위함이다. 5.2 DCLTrace void DCLTrace(pszFormat, ) TRACE 관련매크로에서사용하는함수로사용방법은 printf 와동일하다. DCLDebugSetGlobalReport 나 DCLDebugSetThreadReport 에의해지정된스트림에출력한다. - 7 -
6. 동적메모리디버그 DCL의동적메모리디버깅이활성화되려면 DCL_HAVE_ALLOC_DEBUG가활성화되어야한다. 이매크로가정의되면 DCL_DEBUG는반드시정의되어야한다. DCL_DEBUG가정의되어있어도 DCL_HAVE_ALLOC_DEBUG가정의되지않으면메모리관련디버깅루틴들은활성화되지않는다. DCL의동적메모리할당관련디버깅은메모리블록의종류와스레드정보를유지한다. 6.1 Functions and Operators 동적메모리할당관련디버깅은 malloc, free, realloc, calloc 과 new, new[], delete, delete[] 연산자 에대해서행해지며이들함수와연산자의추적은 DCL_HAVE_ALLOC_DEBUG 가활성화되어있으 면자동으로이루어진다. 동적메모리를조작하는데있어서범하기쉬운실수의첫번째는할당하지않는메모리나혹은해제한메모리를해제하려고시도하는경우이고두번째는매치되지않는연산자의사용이다. 가령 new[] 연산자에의하여할당된객체는반드시 delete[] 연산자에의해파괴되어야한다. delete와 delete[] 연산자는내부적으로동일한 free 함수를사용하지만 delete[] 연산자는배열을이루는모든객체의파괴자 (destructor) 를개별적으로호출하는차이점이있다. DCL 은내부적으로이러한실수들에대한정보를보고한다. 6.2 DCLDebugGetLastAllocPosition DCLCAPI const void* DCLDebugGetLastAllocPosition( unsigned long uthreadid uthreadid 가할당한메모리블록의마지막위치를되돌린다. uthreadid 가할당한메모리가없으면 NULL 을되돌린다. 이함수는 DCLDebugDumpThradMemoryLeak 을사용하여스레드가할당한메모리 블록의리스트를출력하는데있어서범위를지정하기위한목적이있다. 6.3 DCLDebugDumpThreadMemoryLeak DCLCAPI int DCLDebugDumpThreadMemoryLeak( unsigned long uthreadid, const void* pvstartposition, DCLAllocLeakDumpLevel level DCL_NAMESPACE OutputStream* pout uthreadid 가할당한 pvstartposition 에서최근까지의메모리누출을스트림에출력한다. 성공하면메 - 8 -
모리누출항목의개수를되돌리며그값은 0 보다크거나같다. 실패하면 1 을되돌리고이것은출력 을위한버퍼할당오류를의미한다. pvstartposition 은 NULL 일수있으며이경우 uthreadid 가할당한전체메모리블록의리스트를보 고한다. level 은다음값중하나이다. - DCL_ALLOC_DUMP_DEFAULT : DCL 외부에서할당된동적메모리정보와 DCL 내부에서생성되었지만파괴되지않은 Exception 객체를보고한다. - DCL_ALLOC_DUMP_INTERNAL : DCL 내부를포함한모든해제되지않은메모리블록의정보를보고한다. 6.4 DCLDebugDumpGlobalMemoryLeak DCLCAPI int DCLDebugDumpGlobalMemoryLeak( DCLAllocLeakDumpLevel level DCL_NAMESPACE OutputStream* pout 프로세스가할당한전체메모리누출을보고한다. 성공하면메모리누출항목의개수를되돌리며그 값은 0 보다크거나같다. 실패하면 1 을되돌리고이것은출력을위한버퍼할당오류를의미한다. 7. 예외 (exception) 위치보고 7.1 DCL_THROW DCL 은예외가발생했을때 Exception 클래스로부터파생된객체의포인터를던진다 (throw). 따라서 이들예외객체를잡기 (catch) 위해서는적어도다음과같은문장이어야한다. try { AnyException* e = new AnyException; DCL_THROW(e } catch(exception* e) { e->destroy( } 여기에서 DCL_THROW 매크로는 DCL_HAVE_THROW_LOCATION 가활성화되었을경우예외 를던지기 (throw) 전에예외객체 *e 에예외가던져지는소스파일의이름과라인번호를설정한다. 이러 한방법은예외의메시지로부터예외가발생한원인을쉽게발견하도록하는데도움을준다. - 9 -