본문 바로가기
디버그

브레이크 포인트 실전 활용4

by WeZZ 2012. 2. 14.

아래와 같은 코드가 있습니다. 만일 TrueProc2 함수에 브레이크 포인트를 설정하려고 할 경우 어떻게 하나요.

 

1: void foo() {

2:     //...

3:     if(Jurdge())

4:      { TrueProc1(); TrueProc2(); TrueProc3(); }

5:     else

6:         FalseProc();

7:     //...

8: }

 

브레이크 포인트 실전 활용3에서 봤던 조건 중단점 설정으로 Jurdge함수에 브레이크 포인트를 설정해 트리깅 시킨 후, Step Into(F11)을 이용하곤 합니다. 그런데 Step Into를 이용하면 원하지 않는 TrueProc3 함수의 정의부 까지 들어갔다가 나오는 불편함이 있습니다. 물론 TrueProc2 함수의 정의부로 가서 브레이크 포인트를 설정해 놓으면 되긴 합니다만, TrueProc2에서 F12를 이용해 코드 정의부로 들어 간 뒤에 브레이크 포인트를 설정하고 코드를 진행시키는 것 또한 번거롭습니다.

 

위의 예가 일부러 만들어 놓은 것 처럼 저런 상황은 잘 오지 않는다, 혹은 예제 자체가 잘못되었다라고 생각할 수 도 있겠습니다만, 위의 예와 비슷한 상황의 코드는 의외로 자주 마주치게 됩니다. 예를 들면 아래와 같은 겁니다.

 

vector_object.push_back(proxy()->somePointer->Proc(obj.GetStatus(str.c_str())));

 

저렇게 파라미터와 리턴값이 중첩되어 있는 상황에서 Proc 함수만을 디버깅 하기 위해서는 가장 처음 호출되는 c_str()부터 차례로Step Into하거나 Proc의 정의부로 가서 브레이크 포인트를 설정해야 됩니다.

 

위와 같이 중첩된 실행이 한 라인에 중첩되어 있을 경우 의외로 쉽게 디버깅할 실행을 선택할 수 있습니다.

 

 

현재 디버깅되고 있는 라인에서 우측 버튼을 눌러 컨텍스트 메뉴를 호출하면, 항목에 Step Into Specific이 있는데 서브 메뉴 항목을 살펴 보면 해당 라인에서 선택 Step Into 할 수 있는 실행 목록이 표기 됩니다. TrueProc2를 선택하면 별다른 브레이크 포인트 설정이나 코드의 정의부 이동할 필요 없이 단번에 TrueProc2 Step Into하게 됩니다.

 

위의 과정은 마우스를 이용하기 때문에 조금 번거롭습니다. 대부분의 반복적인 디버깅을 하는 개발자들은 최소한의 키보드 타이핑만을 이용하여 원하는 대로 디버거가 동작하기를 기대 하는 편이구요.

 

디버깅 시 Step Into 작업을 반복적으로 하다 보면 디버깅 하는 코드와 디버깅 하지 않는 코드가 서서히 구분 됩니다. 무슨 말인고 하니, 예를 들면,

 

proxy()->somePointer->obj.Proc(str.c_str());

 

이와 같은 코드가 포함된 프로젝트를 반복적으로 디버깅 하다 보면 나중에는 비즈니스 로직이 있는 obj.Proc 맴버를 자주 디버깅 하게 되고, proxy() str.c_str() Step Into를 잘 하지 않게 됩니다. proxy()는 어짜피 간단하게 오브젝트 포인터만 리턴해 주는 녀석일테고, STL코드인 c_str()은 아무 문제도 없는 코드일 뿐만 아니라 대부분의 경우 STL코드를 디버깅 하려는 목적으로 Step Into하지는 않으니까요.

MyClass * a = new MyClass();

 

이와 같은 코드도 번거로운 문제가 될 수 있습니다. 위 코드에서 Step Into하는 이유는 당연히 MyClass의 생성자가 무슨일을 하는지 궁금해서 겠지요? 그런데 VS2008에서 위의 코드를 Step Into할 경우 아래와 같이 new의 정의 코드로 진입하게 됩니다. 따라서Step Into를 두 번 하게 됩니다.

 

 

비주얼 스튜디오는 Step Into를 필터링 해 줄 수 있는 기능을 제공해 주고 있습니다. Step Into 필터링을 적절히 이용하면 선택 실행 Step Into를 위해 컨텍스트 메뉴를 열고 Step Into할 실행을 선택하는 번거로움을 크게 줄여줄 수 있습니다.

 

Step Into 필터링은 레지스트리 키를 이용합니다. 아래의 레지스트리를 확인해 보면 됩니다. VS2008사용자는 10.0대신 9.0, 2005사용자는 8.0으로 접근하면 됩니다.

 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\NativeDE\StepOver

 

비주얼 스튜디오의 기본값이 설정되어 있으므로 레지스트리의 키 값이 없다면 다시 한번 경로를 확인해 봐야 합니다. 64비트의 경우 아래의 키값으로 접근합니다.

 

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\NativeDE\StepOver

 

비주얼 스튜디오의 다음버전도 항상 규칙적인 레지스트리를 통하여 Step Into 필터링 항목을 설정합니다.

 

PRODUCT_ROOT\NativeDE\StepOver

 

VS2003 이사 사용자는 autoexp.datvkdlfdml Execute Control 섹션에 필터링 내역을 추가할 수 있습니다. 레지스트리 작업 후 비주얼 스튜디오상에 반영이 안될 경우 비주얼 스튜디오의 인스턴스를 종료하고 새 인스턴스를 띄우면 반영이 됩니다.

 

제 개발 머신에서의 비주얼 스튜디오의 기본 필터링 값은 아래 처럼 되어 있습니다.

 

 

설정하는 방법의 규칙은 간단합니다. 먼저 Step Into의 필터링에 대한 이름을 정해야 합니다. 값 이름은 문자열이 허용되지만 순서에 영향을 받으므로 일반적으로 ‘1’, ‘101’과 같은 넘버링을 권장하고 있습니다. 숫자가 아니라 문자임을 유의해야 합니다. ‘1’ 보다 ‘101’이 우선순위가 높습니다.

 

실습을 위하여 다음의 항목을 입력해 봅니다.

 

1 = foo=NoStepInto

2 = foo1=NoStepInto

 

위 필터링, NoStepInto의 의미는 foo foo1함수는 Step Into로 정의부 진입을 하지 않는다는 의미 입니다. 아래의 코드를 입력하고 테스트 해 볼까요.

 

1: int foo() {return 0;}

2: int foo1() {return 1;}

3: int foo2() {return 2;}

4: void main()

5: {

6:     int a = foo() + foo1() + foo2();

7: }

 

6번 라인에 브레이크 포인트를 설정합니다. 프로그램을 실행하고 6번 라인에 브레이크 포인트가 트리깅 되면 Step Into(F11)을 누릅니다.

 

만일 위의 필터링 설정이 되어 있지 않다면, Step Into로의 디버깅시 커서의 흐름이 6 > 1 > 6 > 2 > 6 > 3의 순으로 움직일 껍니다. 6번 라인에 foo(), foo1(), foo2()의 순으로 차례대로 실행 진입을 하기 때문입니다.

 

그러나 위의 필터링 설정을 하게 되면 6번 라인에서 Step Into로 진입시 바로 foo2함수로 진입합니다. foo foo1 Step Into(F11)에 필터링 되기 때문입니다. 필터링은 비쥬얼 스튜디오 정규 표현식 양식(Visual Studio Regular Expression Format)으로 아래와 같이 표현할 수 있습니다.

 

RegExp=[No]StepInto

 

정규 표현식에 익숙하지 않은 분들은 두 가지 문자만 유의하면 될 것 같습니다. 마침표 ’.’ 는 아무 한글자로 대체할 수 있고, 애스터리스크 ’*’ 는 연속된 문자로 대체할 수 있습니다. 그리고 정규표현식이라고 하지 않고 비주얼 스튜디오 정규 표현 양식이라고 하는 이유는, 그 어느 곳에도 이에 대한 공식 문서는 없고, 마이크로소프트에서도 보장해 주지 않기 때문입니다. ㅎㅎ 아무튼, 정규 표현식 몇 개만 짚고 넘어가 보면,

 

1 = foo.=NoStepInto

 

위와 같이 설정했다면 foo1, foo2, foo3… 등등이 필터링 됩니다. fooAA와 같은 것을 포함하려면 아래와 같이 설정하면 되리라는 걸 예상할 수 있습니다.

 

1 = foo*=NoStepInto

 

맴버를 필터링 할 수도 있습니다. 아래의 코드를 보면,

 

int foo() {return 0;}

class MyClass {

public:

    int publicM1() {return 1;}

    int publicM2() {return 2;}

};

void main() {

    MyClass obj;

    int v = obj.publicM1() + obj.publicM2() + foo();

}

 

main함수에서 사용된 메소드 publicM1, publicM2, foo MyClass에 대해서 필터링 해주고 싶다면, 아래와 같이 필터링 식을 사용합니다.

 

1 = MyClass\:\:.*=NoStepInto

 

만일 MyClass맴버를 필터링 하되, publicM2는 필터링에서 제외하고 싶다면 아래와 같이 설정하면 됩니다.

 

 

대부분의 프로젝트에서 개발자는 STL안쪽으로 Step Into되는걸 원하지 않습니다. 아래와 같이 필터링 해 준다면 std::list, std::vector, std::basic_string에 대해서 Step Into로 진입하지 않도록 설정됩니다.

 

"1"="std\:\:list.*\:\:.*=NoStepInto"

"2"="std\:\:vector.*\:\:.*=NoStepInto"

"3"="std\:\:basic_string.*\:\:.*=NoStepInto

 

이 정도의 설정 방법으로만 으로도 프로젝트 별로 Step Into 필터링 레지스트리 키를 가지고 있다가 설정한 후 디버깅을 한다면 Step Into시 아주 효율성이 높아질 것입니다. 좀더 자세한 설정 방법은 아래의 표를 참고 하세요.

 

필터링 표현식 키워드

비고

\cid:<C/C++키워드>

지정된 키워드에 대한 [No]StepInto

\funct:<함수명>

함수명에 대한 [No]StepInto

\scope:<클래스, 네임스페이스>

클래스의 네임스페이스 표현

\anything:<문자열>

문자열에 무조건 대응

\oper:<연산자>

재정의된 연산자에 대한 [No]StepInto

 

아래의 표는 몇 가지 예시 입니다.

 

필터링 표현식

의미

\anything:=StepInto

모든 항목에 대해서 StepInto를 허용

\scope:CString.*\:\:.*=NoStepInto

CString 멤버에 대해서 StepInto를 허용하지 않음

\scope:operator\oper:=NoStepInto

오버로딩된 연산자에 대해서 허용하지 않음

"1" = "ATL\:\:.*=NoStepInfo"

"2" = "ATL\:\:CComBSTR::\funct:=StepInto"

CComBSTR의 멤버를 제외한 ATL 네임스페이스의 항목에 대해서 필터링(값의 순서와 CComBSTR의 연산자에 대해 필터링됨을 주의)

1 = \scope:\funct:=StepInto

2 = .*[\<\>].*=NoStepInto

템플릿에 대해 StepInto를 허용하지 않음



출처 : http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=8583&ref=8583