본문 바로가기

도구의발견

[Visual Studio] Break point

버그 없는 프로그램을 만드는 것이 가장 좋은 방법이긴 하지만 현실적으로 처음 부터 그런 것을 만든다는 것은 디버거에 대한 모독이며 여지껏 나왔던 디버깅 관련 책들에 대한 도전이다. 말이 이상하긴 하지만 디버깅이 프로그래밍에서 빠져서는 안되는 중요한 요소라는 것을 강조하고 싶은 것이니 대충 '디버깅은 중요하다' 정도로 이해하고 넘어 가도록 하자.

이렇게 중요한 디버깅을 함에 있어서 Visual studio는 상당히 도움되는 툴들을 제공하고 있는데 그 중에 제일미로 꼽을 수 있는 것이 바로 '브레이크 포인트(Break point)'다. 여기 와서 브레이크 포인트가 뭐냐고 묻는다면...나도 할 말이 없다. 그냥 브레이크 포인트는 알고 있고 대충 visual studio에서 제공하는 기본적인 디버깅 관련 툴들은 사용 할 줄 안다는 가정하에 글을 진행 하도록 하겠다.

그럼 가장 먼저 알아야 할 것은 브레이크 포인트의 설정이다 :

브레이크 포인트 설정

간단한 브레이크 포인트 설정

브레이크 포인트는 가장 간단하게는 소스 코드의 적절한 부분에 가서 <F9>를 누르는 방법과 소스 코드 에디터의 맨 왼쪽 빈 여백을 마우스로 클릭하는 방법이 있다. 너무 쉬운가? 그렇다면 좀 더 빨리 브레이크 포인트를 설정 하는 방법에 대해 알아 보자.

브레이크 포인트 정보 보기

'함수 혹은 클래스 이름을 이용하여 브레이크 포인트 설정'을 하기 전에 먼저 알아 두면 좋을 것이 있다. 바로 '브레이크 포인트 정보'다. 브레이크 포인트들에 관련된 세세한(?) 정보를 보여주는 아주 유용한 창이다. '브레이크 포인트 정보'창을 열기 위해서는 간단하게는 'Ctrl+Alt+B'를 누르는 방법이 있고, 조금 귀찮긴 하지만 메뉴를 이용하여 보기 위해서는 'Debug > Windows > Break Points'와 같이 찾아 갈 수 있다.

해당 윈도우에서 보여 주는 정보로는 브레이크 포인트의 이름(파일 이름과 라인으로 표시), 조건(Condition), 적중 횟수(Hit count) 등등이 있다. 자세한 설명은 다음 장에서 자세히 다루도록 하고 지금은 '브레이크 포인트들의 정보를 한번에 보여 주는 창이 있다' 정도를 알고 지나가도록 하자.

함수 혹은 클래스 이름을 이용하여 브레이크 포인트 설정

잠시 이야기가 다른 곳을 샜지만 다시 본론으로 돌아와서..브레이크 포인트를 걸어야 할 소스가 눈에 바로 보인다면 위의 방법 처럼 마우스나 <F9>를 이용하면 좋지만, 소스 코드 어디에 쳐박혀 있는 한참을 헤메야 하거나, 브레이크 포인트를 걸어야 할 곳이 한 두 곳이 아니라면 '함수 혹은 클래스 이름을 이용하여 브레이크 포인트 설정'하는 법을 알면 편하다.

위에서 설명한 '브레이크 포인트 정보'창을 띄워 보도록하자. Ctrl+Alt+B를 누르면 편하다. 키매핑을 변경했다면 직접 매뉴를 하나씩 클릭하면서 찾아 가도록 하자.

브레이크 포인트 정보 창을 띄웠다면 'New'를 클릭하자. 그냥 그 창에서 마우스 오른 클릭을 하여 '새 브레이크 포인트(New Break point)'를 선택해도 된다. 그럼 당장 눈에 보이는 것이 바로 함수(Function)탭이다.

현재 내가 사용하고 있는 Visual studio는 영문판이라 영어로 나오지만, 한글판을 사용하는 사람에게는 함수라고 한글로 나올것이다. 각설하고 위에 커서가 움직이는 곳에 브레이크 포인트를 걸 함수 이름을 적어 주면 만사 오케이다. 다만 C++같이 대소문자 구분을 하는 경우라면 여기에서도 대소문자를 구분해주어야 하고, 함수이름을 완벽하게 써줘야 한다는 것이다. 어줍잖게 쓰면 '정보가 부족한데도 계속 브레이크 포인트를 걸고 싶니?'라고 물어 온다. 괜시리 서로 피곤해지지 말고 한번에 잘 적어주자.

만일 함수가 중복(오버로드)된 것들이 많이 있다면 특정 클래스 내에서 한계를 긋고 싶기도 할 것이다. 그럴경우 C++이라면 네임스페이스를 찍어주는 것처럼 'ClassName::FunctionName'과 같은 형식으로 입력해주면 된다.  그림이 들어 가서 길어 보이기는 하지만 간단한 내용이다. 함수 이름 똑바로 써라. 그것이 핵심이다.

특정 조건에서만 브레이크 포인트가 걸리도록 하기

디버깅을 하다 보면 매번 브레이크 포인트에 걸리는 것을 원하지 않을 경우가 있다. 예를 들어 for루프를 1000번 도는데 버그는 999번째에만 발생한다고 생각해 보자. 그럼 어떻게 할 것인가? Go next(F10)을 999번 눌러야 할까? 다행이 MS는 그렇게 멍청하지 않아 우리가 그토록 수고 할 필요가 없도록 했다. 간단하게 말하자면 특정 조건을 만족 할 경우에만 디버깅 중에 브레이크가 걸리 도록 할 수 있다는 말이다.

적중 횟수(Hit count)

위(1.2 브레이크 포인트 정보 보기)에서 한번 언급했고, 위의 'New Break point' 이미지에서도 확인했다. 바로 'Hit count'!! 한국말로 하자면 적중횟수가 되겠지만(혹자는 생략 횟수라고도 한다) 그냥 편하게 영어로 진행하도록 하겠다. 이 Hit point 라는 녀석의 용도가 무엇인고 하니, 바로 '지정된 코드가 특정 횟수 이상 반복 되기 전까지는 브레이크 포인트를 활성화 하지 않게 하는 것'이다. 이것을 이용하면 루프내에서 적절한 시기에 브레이크 포인트를 활성화 하는 것이 쉬워 진다.

Hit count를 설정하는 방법은 상당히 간단하다. 첫째로 소스 코드 에디터 왼쪽에 있는 브레이크 포인터(일명 빨간콩)을 오른쪽 클릭하여 'Breakpoint Porperties'를 클릭하는 것이다. 아니면 1.2장에서 설명한 '브레이크 포인트 정보'창에서 원하는 브레이크 포인트에 오른 클릭하여 Properties를 선택 해도 된다.

만일 기존의 브레이크 포인터에 속성을 조절하는게 아니라 새로운 브레이크 포인트를 만드는 것이라면 'New Break point'창에서도 Hit Count를 조절 할 수 있다.

Hit count는 기본값으로 항상 적용되도록 되어 있지만 총 네가지 방법으로 그 값을 조절 할 수 있다 :

적중 횟수 적용 설명
항상 중단(break always) 해당 위치에서는 항상 멈춘다.
적중 횟수가 같은 경우 중단
(break when the hit count is equal to)
해당 위치가 정해진 횟수만큼 실행 되었을 때 멈춘다. 횟수는 1부터 시작한다.
적중 횟수가 배수일 경우 중단
(break when the hit count is multiple of)
해당 위치의 실행 횟수가 x 배수인 경우 멈춘다.
적중 횟수가 크거나 같은 경우 중단
(break when the hit count is greater than or equal to)
적중 횟수가 정해진 횟수에 도달할 때까지 멈추지 않다가 그 후로는 계속 활성화.

간단하게 요약하자면 Hit count 기능을 잘 이용해 디버거에서 프로그램이 중단 되었을 때 프로그램이 얼마나 실행 되었는지 쉽게 유추 할 수 있다는 것이다. 그리고 힘들게 go next를 누를 필요가 없다는 소리이기도 하다.

조건 표현식(Condition)

위에서는 특정 횟수 이상 코드를 실행하게 되면 브레이크 포인트가 활성화 되는 방법에 대해 알아 보았다. 그렇다면 이제는 횟수가 아닌 특정 조건일 경우에만 브레이크 포인트가 활성화 되는 방법에 대해 알아 보도록 하자. 여기서는 Condition이라고 하겠다. 1.2 장에서 이미 언급한 바가 있고, 이제껏 나온 그림들을 봐도 Hit count가 나올 때 항상 빼 놓지 않고 나오는 'Condition' 이 있다. 한글로 번역을 하자면 '조건 표현식' 정도가 되겠지만 메뉴 그대로를 따라 가도록하자(한글 VS에는 뭐라고 나오는지 모르겠다).

이 'Condition'이라는 것의 용도가 무엇인고 하니 특정 조건을 만족하거나, 마지막으로 비교된 후로 값이 변경되었을 때만 브레이크 포인트를 활성화 시킬 수 있는, 우리들의 디버깅 시간을 눈물나게 고마울 정도로 줄여 주는 유용한 툴인 것이다.

일반적으로 'Condition'은 우리가 if 문안에서 생각할 수 있는 표현식을 지원한다. 그리고 지역 변수나 전역 변수들은 현재 실행 중인 컨텍스트에서 평가하기 때문에 이 표현식에서 사용이 가능하다. 기본적으로 조건식이 참인 경우 브레이크 포인트를 활성화 시키도록 되어 있으나 간단한 변경을 통해서 값이 변경되는 경우에 브레이크 포인트를 활성화 시킬 수 있다.

참고 할 사항은 조건이 처음으로 평가되는 순간이 조건을 입력 할 때가 아니라, 조건이 처음으로 실행 되는 순간이라는 것이다. 하지만 조건은 아직 한번도 평가된 적이 없기 때문에 디버거는 내부에 조건식에 필요한 정보를 저장하지 못하고 있다. 결국 브레이크 포인트가 활성화 되기 위해서는 해당 조건이 두번 실행 되는 경우다.

간단 예를 만들기 위해 특정 상황을 가정해 보자. 멀티 쓰레드를 이용하는 어플리케이션을 디버깅을 하다보면 여러 쓰레드에서 동일한 코드를 호출하여 쓰레드 하나 마다 디버깅이 멈춰져 매우 귀찮음을 느낀 사람들이 있을 것이다. 특정 쓰레드일 경우일 경우만 해당 브레이크 포인트를 활성화 시키는 조건식을 만들어 보도록 하자 :

  1. 먼저 쓰레드 아이디를 알아야 한다. 쓰레드 아이디는 디버깅 중에만 알 수 있다. 디버깅을 시작하고 'Ctrl+Alt+H' 나 'Debug > Windows > Threads' 를 선택하면 쓰레드 정보를 보여주는 창을 띄울 수 있다. 해당 정보 중에서 필요한 쓰레드(이름이라던지 location이라던지 활성화 화살표를 참고 하면 알 수 있다)를 찾아 아이디를 외운다(아니면 어디 적어 둬도 된다).
  2. 조건식을 입력 하는 창(Breakpoint Condition)에서 '*(long*)($TIB+0x24) == <thread id>' 조건식을 입력한다. $TIB는 쓰레드 관련 정보가 저장되어 있는 레지스터를 나타내고, 거기에 0x24를 더해주는 것은 나중에 쓰레드 정보 구조체를 살펴 보면 알 수 있을 것이다. 지금은 그냥 그러려니 하고 넘어가도록 하자.
  3. 마지막으로 디버깅을 계속 진행 하면 위에서 지정한 쓰레드 아이디를 가진 쓰레드에서만 함수가 호출 되는 것을 확인하자.

이 외에도 디버깅을 하는데 도움을 주는 도구들이 많이 있다. 예를 들자면 위에서 언급한 $TIB 같은 레지스터 같은 것들이다. 기회가 닿는다면(내가 게으름을 이긴다면...) 다음 번에는 레지스터를 이용한 디버깅에 대해서 다뤄보고 싶다.

참고 : Debugging Applications for Microsoft .Net and Microsoft Windows

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!