디버깅의 어려움과 즐거움

2008. 6. 16. 08:48개발

 

Visual Studio 2005 로 개발/운영 되어 오던 프로덕트를 Visual Studio 2008로 전환하는 작업을 얼마 전부터 진행 중에 있습니다.

2005 2008 은 내부 기반 구조에 있어 큰 차이가 없어서 금방 컨버젼이 끝났고 실제 런타임에서 정상적으로 실행이 되는 지 테스트를 하다가 몇몇 화면에서 프로그램이 비정상적으로 종료되는 문제를 발견했습니다.


디버그에서는 이미 정상적으로 실행됨을 확인한 상태였기 때문에 릴리즈 버전의 빌드에 문제가 있나 하고 여러 번 재 빌드를 했지만 (재 빌드 시간이 1-2시간 그냥 갑니다 --)  여전히 문제가 해결되지 않더군요.

워낙 여러 개발자가 동시에 프로그램을 수정 중이라 이런 일이 비일비재하긴 했지만, 이전까지 멀쩡하던 프로그램이 계속 죽어 나가는 게 좀 이상했습니다.

게다가 디버깅에서는 멀쩡한 상태라....

 

다행인 것은 프로그램이 항상 죽어 준다는 점입니다.

여태까지 경험으로 가장 어려운 디버깅은 재현하기 힘든 비정상 종료였습니다.

그나마 재깍 재깍 죽어주면 어떻게든 해결할 방법이 있거든요. 시간만 충분히 주어진다면요.

 

아직 Visual Studio 2008 로 전환하는 초기 단계라 디버깅 정보를 담고 있는 Symbol Server나 관련 PDB 파일을 빌드 서버에서 구하기 힘든 상황이어서 Dump를 이용하여 Call Stack 을 뒤지거나 하기 힘들었습니다.

 

그래서 가장 무식한 방법으로 프로그램이 죽을 만한 곳에 MessageBox() 를 수십 군데 추가해서 낚싯대를 드리우고 낚이기를 기다렸습니다.

낚시를 엉뚱한데 놓다 보니 이 작업도 역시 몇 시간 걸렸습니다. 얼마 후 문제가 있을만한 곳 근처를 발견하고, 삽질을 반복한 결과 아래 라인에서 프로그램이 죽는다는 것을 발견했습니다.

 

vector<datas>& item = m_Data.vtItems.at(i);

당연히 변수 i 는 정상적인 범위를 가리키고 있었기 때문에 고개를 갸우뚱하게 만들더군요.

게다가 Visual Studio 2005 버전에서는 전혀 문제가 없었던 코드였고, 디버깅에서도 정상적으로 수행되지 더 난감했습니다.

 

m_Data 는 대략 아래와 같이 생겨먹었습니다.(설명을 위해 대부분 축약했습니다)


struct TEST_STRUCT

{

           char szCode[100];

           ....

           vector<datas> vtItems

};


 

더 희한한 것은 vector 에 데이터를 정상적으로 채운 직 후 다시 데이터를 꺼내기만 해도 죽는 다는 것을 발견했습니다.


datas item;

// item 에데이터를채운다.

m_Data.vtItems.push_back(item);

 

datas& a = m_Data.vtItems.at(0);


이쯤 되니 별의별 과거 경험이 다 떠올랐습니다.

분명 현재 코드 상의 특별한 문제가 없었기 때문에 과거에 겪었던 문제들 - 특히 DLL 에서 만든 STL 컨테이너 데이터를 넘겨 받을 발생하던 Crash 문제들이 하나 둘 떠 올랐습니다.

 

다행히 이 경우는 호출하는 쪽이나 구현 하는 쪽이나 모두 동일한 런타임 라이브러리에서 구현되어 있기 때문에 그 문제는 아니었고, m_Data 가 뭔가에 의해 오염되었다는 느낌을 받았습니다.

 

결국 찾아낸 문제의 원인은 늘 그렇듯 허무하게 결론났습니다.


memset(&m_Data, 0, sizeof(TEST_STRUCT));

클래스 생성자에서 딴에는 데이터를 정상적으로 초기화 한답시고 위와 같이 memset 으로 데이터를 0 으로 초기화 하는 코드가 들어있었습니다.

 

struct 내부 변수에 CString 과 같은 클래스가 들어 있을 경우 memset() 을 하게 되면 프로그램이 Crash 된다는 사실을 몇번이나 겪어서 그동안 이런 경우를 거의 발견하지 못했었는데 Visual Studio 2008 로 컴파일 하다가 정말 우연히 발견하게 된 것입니다.

 

이번 문제의 경우 아래와 같은 여러 문제가 섞여 있어 문제 파악에 애를 먹었습니다.

 

  • Visual Studio 2005 프로덕트에서 문제가 보고되지 않았다. - 문제가 없는 코드라는 의미는 아닙니다. 오히려 전혀 엉뚱한 곳에서 프로그램을 죽게 할 수도 있었을 것입니다. 다행히 Visual Studio 2008 에서 이 문제를 발견해서 Visual Studio 2005 프로덕트 환경도 수정할 수 있게 되었습니다.
  • 디버그에서 아무런 이상 증상이 없다. - 이런 경우는 참 많습니다. 가장 많이 겪는 경우는 변수를 초기화하지 않았을 때 디버그에서는 멀쩡하다가 릴리즈에서 사망하는 경우입니다. 기타 경우에도 이런 경우가 허다해서, 늘 릴리즈 버전에서 테스트를 철저히 해야 한다는 각오로 임하고 있습니다.
  • 문제의 원인과 발생지점이 상이했다 - 생성자에서 차라리 죽었다면 찾기 쉬었을 것입니다. 하지만 데이터를 추가하는 push_back 에서도 발생하지 않다가 데이터를 Get 하는 곳에서 죽어서 문제 파악에 얘를 먹었습니다. 아마 Crash Dump 자료가 있다 하더라도 원인 파악이 힘든 문제임에 틀림없습니다.

 

늘 겪는 문제지만, 디버깅은 결코 쉬운 분야가 아닙니다.

상당한 개발자의 직관과 경험이 필요하고, 경우의 수를 헤아릴 수 있어야 가능한 분야라고 믿고 있습니다.

오류의 원인을 찾아내는데 소홀한 개발자 치고 안정된 프로덕트를 운영할 수 있는 개발자는 없으니까요.

수수께끼 같은 문제를 풀어가는 과정이 좀 힘들고 지겹고, 가끔은 엉뚱한 데 파다가 시간도 허비하고 해서 좀 거시기 하긴 합니다만 오류를 찾았을 때 기쁨도 역시 개발자만이 알 수 있는 오아시스가 아닌가 싶습니다.




  • 프로필사진
    BlogIcon esstory2008.06.16 09:48 신고

    음.. 또 믹시 추천 버튼을 잘못 눌렀네요.. 이거 낙장불입인가요?

  • 프로필사진
    BlogIcon object2008.06.16 10:44

    저거 조심해야하죠. 클래스 멤버 객체가 있을 땐 memset으로 하는 건 위험합니다. vector의 어떤 멤버 변수가 희한하게 문제를 일으킨 것 같네요.

    • 프로필사진
      BlogIcon esstory2008.06.16 10:52 신고

      저희도 조심한다고 하는데도 같은 문제가 반복되네요.
      기본 struct 에는 클래스가 없어서 멀쩡하다가 어느날 누군가 유지보수 하면서 슬쩍 구조체에 뭔가를 추가하는 경우도 제법 있어서..
      다행히 2008 에서 처럼 콱 죽어 주면 그나마 다행인데 말입니다.

  • 프로필사진
    BlogIcon 김재호2008.08.15 18:06

    아직 2005쓰는데, 2008은 개인적으로만 쓰고있어요.
    제가 하는 프로젝트도 빨리 2008로 바꾸고 싶어집니다^^

    • 프로필사진
      BlogIcon esstory2008.08.16 11:56 신고

      며칠전에 SP1 도 공식적으로 나와서 그동안 2008 에 있던 버그들도 상당히 많이 수정되었더라구요.
      늘 그렇지만, 신상이 더 좋은게 많습니다 :)