버그 잡기
페이지 정보
본문
프로그램 하다 보면 가장 짜증날 때가 분명히 일어날 수 없는 일이라고 생각하고 있는 데 눈앞에서 버젓이 일어날 때 입니다. 버그죠. 프로그래머의 실수이든, 운영 체제의 실수이든(근데 프로그래머 실수일 가능성이 높죠. 운영 체젠 비싸게 돈주고 파는 거니까 오죽이나 잘 만들어 놓았겠습니까?) 버그 때문에 잠 설친 적이 하루 이틀이 아니죠. 그래서 제가 쓰는 디버깅 방법을 몇 자 적어 볼까 합니다.
우선, 모든 변수를 믿지 않죠. 분명 1,2 둘중에 하나다..라고 하더라도 믿지 않습니다. 그래서 switch 문을 쓸 땐 항상 default, if 문을 쓸 땐 else를 넣죠. 함수를 만들 경우, 전 우선 될 수 있으면 모든 함수의 리턴형을 불린으로 합니다.
CRgn 뭐 이런 걸로 리턴해야 한다면, 포인터 있으니까 포인터로 넘겨 버리죠. 그렇게 한 다음, 함수의 첫머리에서 하는 일은 항상 그 함수 내에서 쓸 값들(보통 파라미터가 되겠죠)의 유효성 검사 입니다. 말이 멋있는 데, 사실 별거 없구, if문으로 쭉 검사하는 거죠. 그 땐, 파일 첫머리에 #define _ENABLEERRORMESSAGE_ 이렇게 해주고, if문 안에서
if(어쩌구 저쩌구 해서 false를 리턴해야 한다면){
#ifdef _ENABLEERRORMESSAGE_
AfxMessageBox(_T("에러에요. ~~~가 잘못되었네요^^"));
#endif
#ifdef _DEBUG
TRACE2(_T("%s(%d) : 에러 발견!! 내용 - 어쩌구~~~"),__FILE__,__LINE__);
#endif
return false;
}
이렇게 해주죠. TRACE구문은 컴파일 모드에 따라 알아서 컴파일 되겠지만, 명확히 할려구 저렇게 하구요. 저렇게 하고, 테스트 끝나면
#define _ENABLEERRORMESSAGE_
부분을 주석처리하죠. TRACE구문을 저렇게 하면 저 구문이 디버그 아웃풋 창에 나올 때 그 줄을 더블클릭하거나 F4를 누르면 저 줄로 캐럿이 이동하더군요. 또 컴파일 끝나면, Control + F5보단, 좀 느리지만, F5를 눌르죠.
그렇게 해서 에러나면, 메세지 박스 뜨잖아요. '취소' '재시도' '무시' 뭐 이런거 나오는 데, 그 때 '재시도' 누르면 어디서 에러가 났는 지 그 부분으로 이동하죠.
그 때 어쩔 땐 어셈 코드같은 게 나오는 데, 그 땐 디버그 툴 바 중에 CallStack창 띄워서 함수가 실행된 순서를 거꾸로 찾아가죠. 어셈 코드가 아니면 보통 ASSERT구문에서 걸리게 되는 데 그걸 보고 어디가 잘못되었는 지 알 수 있죠. 가령
ASSERT(::IsWindow(hWnd));
에서 걸렸다면, 아직 윈도우가 만들어지지 않은 경우죠.
이런 건, PreCreateWindow같이, 윈도우를 만들기 전에 실행되는 함수에서
GetClientRect();
같이 윈도우의 크기를 얻어갈려면 발생하겠죠. 포인터를 잘못 쓸땐 그건 좀 어렵더라구요... mfc함수 중에도 불린형 값을 리턴하는 함수를 쓰면 그뒤 바로 리턴값을 확인한답니다. 한번은 윈2000에서
CRgn rgn;
ASSERT(rgn.CreateRectRgn(0,0,0,0));
한 적이 있었는 데, 윈98로 가니까 에러가 나더라구요. 이 부분에서 걸린 겁니다.
윈98은 좌표값을 모두 0으로 주면 영역이 만들어지지 않았던 겁니다. 어쩔 땐 나만의 ASSERT구문을 만들기도 하죠. 이런 식으로도 에러를 못찾으면 적당한 곳에 브레이크포인트 걸어 놓구, 쭉 가다가 에러나는 곳에서 GetLastError();구문을 써서 무엇이 잘못되었는 지 알아가기도 합니다. 예외 처리를 많이 해주면 처음엔 확실히 오래 걸리지만 확실히 디버깅하긴 편하더라구요.
우선, 모든 변수를 믿지 않죠. 분명 1,2 둘중에 하나다..라고 하더라도 믿지 않습니다. 그래서 switch 문을 쓸 땐 항상 default, if 문을 쓸 땐 else를 넣죠. 함수를 만들 경우, 전 우선 될 수 있으면 모든 함수의 리턴형을 불린으로 합니다.
CRgn 뭐 이런 걸로 리턴해야 한다면, 포인터 있으니까 포인터로 넘겨 버리죠. 그렇게 한 다음, 함수의 첫머리에서 하는 일은 항상 그 함수 내에서 쓸 값들(보통 파라미터가 되겠죠)의 유효성 검사 입니다. 말이 멋있는 데, 사실 별거 없구, if문으로 쭉 검사하는 거죠. 그 땐, 파일 첫머리에 #define _ENABLEERRORMESSAGE_ 이렇게 해주고, if문 안에서
if(어쩌구 저쩌구 해서 false를 리턴해야 한다면){
#ifdef _ENABLEERRORMESSAGE_
AfxMessageBox(_T("에러에요. ~~~가 잘못되었네요^^"));
#endif
#ifdef _DEBUG
TRACE2(_T("%s(%d) : 에러 발견!! 내용 - 어쩌구~~~"),__FILE__,__LINE__);
#endif
return false;
}
이렇게 해주죠. TRACE구문은 컴파일 모드에 따라 알아서 컴파일 되겠지만, 명확히 할려구 저렇게 하구요. 저렇게 하고, 테스트 끝나면
#define _ENABLEERRORMESSAGE_
부분을 주석처리하죠. TRACE구문을 저렇게 하면 저 구문이 디버그 아웃풋 창에 나올 때 그 줄을 더블클릭하거나 F4를 누르면 저 줄로 캐럿이 이동하더군요. 또 컴파일 끝나면, Control + F5보단, 좀 느리지만, F5를 눌르죠.
그렇게 해서 에러나면, 메세지 박스 뜨잖아요. '취소' '재시도' '무시' 뭐 이런거 나오는 데, 그 때 '재시도' 누르면 어디서 에러가 났는 지 그 부분으로 이동하죠.
그 때 어쩔 땐 어셈 코드같은 게 나오는 데, 그 땐 디버그 툴 바 중에 CallStack창 띄워서 함수가 실행된 순서를 거꾸로 찾아가죠. 어셈 코드가 아니면 보통 ASSERT구문에서 걸리게 되는 데 그걸 보고 어디가 잘못되었는 지 알 수 있죠. 가령
ASSERT(::IsWindow(hWnd));
에서 걸렸다면, 아직 윈도우가 만들어지지 않은 경우죠.
이런 건, PreCreateWindow같이, 윈도우를 만들기 전에 실행되는 함수에서
GetClientRect();
같이 윈도우의 크기를 얻어갈려면 발생하겠죠. 포인터를 잘못 쓸땐 그건 좀 어렵더라구요... mfc함수 중에도 불린형 값을 리턴하는 함수를 쓰면 그뒤 바로 리턴값을 확인한답니다. 한번은 윈2000에서
CRgn rgn;
ASSERT(rgn.CreateRectRgn(0,0,0,0));
한 적이 있었는 데, 윈98로 가니까 에러가 나더라구요. 이 부분에서 걸린 겁니다.
윈98은 좌표값을 모두 0으로 주면 영역이 만들어지지 않았던 겁니다. 어쩔 땐 나만의 ASSERT구문을 만들기도 하죠. 이런 식으로도 에러를 못찾으면 적당한 곳에 브레이크포인트 걸어 놓구, 쭉 가다가 에러나는 곳에서 GetLastError();구문을 써서 무엇이 잘못되었는 지 알아가기도 합니다. 예외 처리를 많이 해주면 처음엔 확실히 오래 걸리지만 확실히 디버깅하긴 편하더라구요.
댓글목록
등록된 댓글이 없습니다.