[Develope]/Windows

DrWatson, 닥터왓슨

하늘을닮은호수M 2008. 7. 14. 16:39
반응형

출처 : http://serious-code.net/moin.cgi/DrWatson

  1. 닥터 왓슨은 무엇인가?
  2. 닥터 왓슨 설정하기
  3. 닥터 왓슨 로그를 비주얼 스튜디오를 이용해서 무식하게 보는 방법
    1. 소스 코드 준비하기
    2. 디버그 심볼 생성하기
    3. MAP, COD 파일 생성하기
    4. 고정 로드 베이스 16진수 주소 지정하기
    5. 닥터 왓슨 로그의 내용
    6. 다운된 곳을 찾기 예제


 


1 닥터 왓슨은 무엇인가?

    유닉스 계열에서는 애플리케이션이 다운되었을 때, 코어 파일이 기본적으로 남지만, 윈도우에서는 그렇지 않다. 코어 파일이 남지 않는다고 해서, 클라이언트는 물론이고 서버도 애플리케이션을 돌리는 컴퓨터에 일일이 디버깅을 위해 비주얼 스튜디오를 깔 수도 없는 일이다. 바로 이럴 때 크래쉬 덤프를 남겨주는 프로그램이 닥터 왓슨이다. 즉 닥터 왓슨은 애플리케이션이 다운되었을 때, 크래쉬 덤프를 남겨주는 프로그램이란 말이다.

    닥터 왓슨은 기본적으로 활성화 되어 있지 않다. 활성화하기 위해서는 명령창에서 drwtsn32 -i라고 입력하면 된다. 이는 닥터 왓슨을 기본 디버거로 설정한다.

    닥터 왓슨이 생성하는 drwtsn32.log 파일이 어디 생성되는가는 세팅을 통해서 바꿀 수 있다. 명령창에서 drwtsn32라고 입력하면 닥터 왓슨 GUI를 볼 수 있다.

    figure01.gif
    그림 1

    각각의 세팅에 관한 부분은 닥터 왓슨 헬프를 참고하기 바란다.


2 닥터 왓슨 설정하기

  1. 기본 디버그 심볼 설치
    1. [WWW]여기로 가서 Windows 2000 기본 심볼들을 다운받는다. 약 100메가 가량 된다.
    2. Win2000_Sym_XXX.exe를 클릭해서 압축을 푼다.
    3. dbg.htm을 클릭한다.
    4. 왼쪽 프레임에 있는 "디버깅 도구 설치" 메뉴를 클릭하면, 오른쪽 프레임의 "심볼 설치" 부분이 보일 것이다. 클릭한다.
    5. 파일 저장 대화 상자가 뜨는데, "저장"을 선택하지 말고, "열기"를 선택한다. 라이센스 대화 상자가 뜨면, "YES"를 선택한다. -_-;
    6. 설치할 디렉토리를 묻는 대화 상자가 뜰 것이다. 기본값은 "C:\Winnt\Symbols"인데, 바꿔봤자 좋을 것 없으므로 왠만하면 그냥 놔두자. 디렉토리를 설정했으면 OK 버튼을 클릭한다.
    7. 파일 카피 대화 상자가 보일 것이다. 이 카피가 모두 끝나면 설치가 완료된 것이다.

      같이 딸려오는 디버깅 툴은 설치하지 말기를 바란다. 나중에 다른 버전으로 설치할 것이다.

  2. 서비스팩 디버그 심볼 설치
    1. [WWW]여기에 가서 해당하는 OS, 서비스팩 버전에 맞는 파일을 다운받는다.
    2. 다운받은 파일을 더블클릭하면, 압축을 풀고 설치할 디렉토리를 묻는데, 기본 디버그 심볼을 설치한 디렉토리를 지정한다.

  3. "내 컴퓨터 -> 등록 정보 -> 고급 -> 환경변수 -> 시스템 변수"로 가서 _NT_SYMBOL_PATH란 변수를 선언하고, 그 값을 커널 디버그 심볼이 있는 위치 + 애플리케이션 디버그 심볼이 있는 위치로 설정한다.

      예)
      _NT_SYMBOL_PATH = %systemroot%\Symbol;D:\Cuvit\Sbin 
      

  4. WinDbg 설치
    1. [WWW]여기에 가서 "Debugging Tools for Windows"를 최신 버전으로 받는다.
    2. 받은 EXE 파일을 더블클릭해서 설치한다.

  5. 닥터 왓슨 설정하기
    1. 명령창에서 drwtsn32 -i라고 입력해 닥터 왓슨을 기본 디버거로 설정한다.
    2. 다시 명령창에서 drwtsn32라고 입력해, GUI를 띄운다.
      1. 심볼 테이블 덤프, 모든 스레드 컨텍스트 덤프를 끈다.
      2. 기존 로그 파일에 추가, 시각적 알림, 크래시 덤프 파일 만들기를 켠다.
      3. 로그 파일 경로와 크래시 덤프 위치를 자알~ 지정해준다.

  6. 덤프 파일이 생성되면, 이전에 설치한 WinDbg 또는 비주얼 스튜디오를 이용해 XXX.dump 파일을 열면 된다. 단 둘 다 소스 디렉토리를 지정해줘야 어셈블리를 제외한 예쁜 C++ 코드를 볼 수 있으므로 주의할 것. 별로 지장은 없다만.


3 닥터 왓슨 로그를 비주얼 스튜디오를 이용해서 무식하게 보는 방법

    3.1 소스 코드 준비하기

      닥터 왓슨 로그가 비록 많은 정보를 포함하고 있지만, 제대로 세팅해주지 않으면 그냥 16진수의 나열일 뿐이라서 제대로 된 정보를 끌어내기가 힘들다. 그러므로 소스를 다음과 같은 방법으로 준비해야한다.

      1. 디버깅을 원하는 EXE/DLL의 디버그 심볼 파일을 릴리즈 버전으로 생성한다.
      2. 프로그램의 MAP 중간 파일과 COD 중간 파일을 생성해서 보관한다. (이 파일들에 관해서는 아래에서 설명한다.)
      3. EXE/DLL의 고정 로드 베이스 16진수 주소(fixed load base hexadecimal address)를 지정한다.

      대부분의 개발자들이 MAP, COD 중간 파일을 생성하지 않는다. 프로그램이 다운되었을 때 이들 파일이 요긴하게 쓰인다는 것을 기억하고, 이들 파일을 생성하기를 바란다.

    3.2 디버그 심볼 생성하기

      성능상의 문제 때문에, 비주얼 스튜디오의 릴리즈 버전 기본 빌드 옵션은 어떤 디버그 정보도 생성하지 않기로 되어있다. 그러므로 링커 및 컴파일러 탭에서 디버그 심볼을 생성하도록 해줘야한다.

      링커 탭
      그림2와 같이 Generage debug info 항목을 체크해준다.

      figure02.gif
      그림 2

      컴파일러 탭
      C/C++ 탭에서 그림 3과 같이 Debug info 항목을 "Program Database"라는 것으로 고른다.

      figure03.gif
      그림 3

      비주얼 스튜디오는 이외에도 코드 브라우징을 위한 정보를 생성할 수 있는 옵션을 제공한다. 이 정보는 함수, 변수, 선언 등을 찾을 때 도움이 된다. 이에 대해서는 뒤에서 더 언급하겠다.

    3.3 MAP, COD 파일 생성하기

      MAP 파일은 애플리케이션에 포함된 모든 함수의 16진수 주소를 가지고 있다. 그리고 이를 이용해 애플리케이션이 다운되었을 때, 어느 함수에서 다운되었는지를 알아낼 수 있다. MAP 파일을 생성하기 위해서는 그림2에서처럼 링커탭에서 "Generate mapfile"이란 항목을 체크한다.

      MAP 파일에 좀 더 유용한 정보를 추가하기 위해 수동으로 스위치를 추가할 수 있다. 예를 들어 /mapinfo:exports라고 추가하면, 실행 파일이 외부로 export한 함수에 대한 정보를 추가된다. 또한 /mapinfo:lines라고 추가하면, 라인 정보가 추가된다. 이는 그림4처럼 C/C++탭의 "Project Options" 항목에서 추가할 수 있다.

      figure04.gif
      그림 4

      COD 파일은 소스 코드, 어셈블리 코드, 메모리 주소를 함께 가지고 있다. 이 파일을 이용하면, MAP 파일을 통해서 알아낸 함수의 어느 부분에서 에러가 발생했는지를 알 수 있다. COD 파일을 생성하기 위해서는 그림 5에서처럼 C/C++ 탭에서 /FAcs 스위치를 추가해야한다.

      figure05.gif
      그림 5

    3.4 고정 로드 베이스 16진수 주소 지정하기

      로드 베이스를 지정하지 않으면, 닥터 왓슨 로그를 이용하기가 좀 더 어려워진다.

      닥터 왓슨 로그의 가장 중요한 부분은 다운이 어디에서 일어났는지를 가르키는 16진수 주소이다. 로드 베이스 주소를 지정함으로서 MAP 파일과 연계해, 다운된 곳이 어디인지를 알아내는 일이 좀 더 쉬워진다.

      예를 들어 EXE 파일 하나와 DLL 파일을 하나 생성했다고 하자. EXE 파일은 DLL 안에 있는 함수를 매우 자주 사용한다. EXE 파일의 로드 베이스는 지정했으나, DLL 파일의 로드 베이스를 지정하는 일은 잊어버렸다고 하자. EXE 파일이 DLL 내부 함수의 오류로 인해 다운되면, 닥터 왓슨 로그의 주소(DLL의 주소)와 MAP 파일을 통해서 알 수 있는 주소는 서로 맞지 않는다. 이것은 커널이 매 실행시 DLL을 다른 로드 베이스에다 매핑시키기 때문이다. 이 경우 어디서 다운되었는가를 아는 것은 불가능에 가깝다.

      로드 베이스를 지정하기 위해서는 그림6에서처럼 링커 탭에서 "Base address" 항목에다가 16진수 숫자를 넣어야한다.

      figure06.gif
      그림 6

      고정 로드 베이스 주소값에 대한 부분은 "Debugging Application" 책을 참고하기 바란다.

    3.5 닥터 왓슨 로그의 내용

      기본적으로 닥터 왓슨 로그는 쌓인다. 애플리케이션이 다운될 때마다 닥터 왓슨은 로그 파일에다 해당하는 내용을 추가한다. 그래서 로그 파일은 각 모듈에 관한 심볼 덤프까지 켜놓았을 경우 상당히 커질 수 있다. 이에 관한 사항은 그림1을 참고하기 바란다.

      로그의 제일 앞부분은 어떤 프로그램이 다운되었는가를 보여준다. 프로세스 ID와 실행중이던 프로세스 목록을 통해 어느 프로그램이 다운되었는가를 알 수 있다.

      컴퓨터 이름, 사용자 이름, CPU 숫자 또한 알 수 있는데, 클라이언트의 경우에는 약간 참고할 수도 있겠다.

      상태 덤프(State Dump)는 프로그램이 다운되었을 때의 CPU 레지스터를 보여준다. 어셈블리를 잘 아는 사람 아니면 솔직히 별 도움 안 되는 정보다.

      그 아랫부분의 스택 역추적(Stack Trace) 부분은 함수를 불린 순서대로 보여준다. 제일 윗부분이 다운된 함수다.

      그리고 가장 중요한 부분이 스택의 상태를 좍 보여주는 부분이다. 이 부분에는 항상 오류(FAULT)라고 라벨이 붙어있다. 이 부분이 바로 다운된 곳이다.

    3.6 다운된 곳을 찾기 예제

      예제를 통하는 것이 닥터 왓슨을 이해하는데 도움이 될 것이다. 아래의 코드는 보다시피 같은 메모리 블록을 두번 해제해서 크래쉬를 발생시키는 코드다. 이 프로그램을 닥터 왓슨이 설치된 곳에서 실행하면 로그 파일이 생성된다.

      char* pch1 = 0;  
      char* pch2 = 0;  
      pch1 = malloc( 20 );  
      pch2 = malloc( 20 );  
      pch1 = pch2;  
      free( pch1 );  
      free( pch2 ); 
      

      닥터 왓슨 로그를 통해 어느 프로그램이 다운되었는지를 알 수 있다. "오류(FAULT)"라고 표시된 부분의 주소를 보자. 이 주소가 그림 7에서처럼 0x60f22be7였다고 하자.

      figure07.gif
      그림 7

      MAP 파일에서 다운된 주소보다 작은 주소값을 가지고 있는 함수 중에서 가장 큰 주소값을 가지고 있는 함수를 찾자. 그림8은 MAP 파일의 내용을 보여준다. ?Process@CInstance 함수의 주소는 CInstance::Process 함수의 주소와 일치한다는 것을 알 수 있다. 이 함수는 cinstanc.obj 모듈 내부에서 0x60f22bc0의 주소로 로드되었다는 것을 알 수 있다.

      figure08.gif
      그림 8

      다운된 주소 0x60f22be7은 0x60f22c40보다는 작고, 0x60f22bc0보다는 크다. 따라서 프로그램은 CInstance::Process 함수 내부에서 다운된 것을 알 수 있다. 그리고 이 함수는 cinstance.cpp 파일에 있다.

      정확이 어느 라인에서 다운되었는지를 알기 위해서는 다운된 주소에서, MAP 파일에서 발견한 함수의 주소를 뺀다. 이 예에서는 크래쉬 주소에서 CInstance::Process 함수의 주소를 뺀다. (0x60f22be7 - 0x60f22bc0 = 0x27).

      MAP 파일에서 찾은 모듈에 해당하는 COD 파일을 본다. MAP 파일에서 찾은 함수에서 시작해서 뺀 결과 값만큼의 라인을 따라 내려간다. 이 예에서는 그림9에서처럼 cinstance.cod 파일을 열어서 ?Process@CInstance 함수를 찾은 다음, 0x27 값만큼 내려간다.

      figure09.gif
      그림 9

      그림 7과 9를 비교해 보면, 둘 다 0x27 2b 59 0c를 포함하고 있는 것을 볼 수 있다. 결국 이 명령(instruction)은 그림 9에서처럼 138 라인에 포함되는 것을 알 수 있다.

      138 라인에 있는 이 Process() 함수는 실제로는 다른 모듈에 있다. 이 모듈은 빌드 때 생성했던 browse information을 이용해서 쉽게 찾을 수 있다. 마우스 커서를 Process() 함수 위에 놓고, 오른쪽 버튼을 클릭한 다음, "선언으로 이동" 메뉴를 이용하면 Process() 함수가 선언된 곳으로 금방 이동할 수 있다. 바로 이 함수는 최초의 메모리 해제와 관련된 문제가 있었던 코드라는 걸 알 수 있다.

      figure10.gif
      그림 10

      figure11.gif
반응형

'[Develope] > Windows' 카테고리의 다른 글

BoundsChecker 사용법  (0) 2008.11.25
Dr.Watson 로그 파일 사용  (1) 2008.07.18