Dr.Watson
2006/08/22 08:07

Dr.watson을 이용하여 에러가 발생하는 곳을 찾아보자.

먼저 윈도우 98에서는 drwatson.exe를 실행시키면 되고 이렇게 만들어진 추적 파일은 \Windows\Drwatson 폴더에 .wlg 파일로 저장된다.
NT계열에서는 drwtsn32를 실행시키고 C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson 에 .log 파일로 저장된다.

Dr. Watson 설정
시스템은 레지스트리 편집기의 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug

Auto 항목이  1 이면 시스템이 자동으로 디버거를 시작하고 메시지 상자를 만들지 않는다.
시스템에 Windows가 설치된 경우 Auto 항목의 값은 기본값 1로 설정되고 Debugger 항목의 값은 Dr. Watson을 시작하는 명령을 지정한다. 이는 프로그램 오류가 발생했을 때 Windows용 Dr. Watson이 자동으로 오류를 진단하고 해당 진단 정보를 로그에 기록
하지만 NET이 설치되면 값은 아래와 같이 설정된다.

NET경우일때 registry값
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Auto"="0"
"Debugger"="\"C:\\WINDOWS\\system32\\vsjitdebugger.exe\" -p %ld -e %ld"
"UserDebuggerHotKey"=dword:00000000

VC++ 6.0 일때
"Debugger"="C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin\msdev.exe" -p %ld -e %ld

Dr.Watson을 설정하고 나서...
예제로 아래와 같이 NULL 포인터에 값을 대입하면 예외가 발생한다.
char* pch1 = NULL;
...
*pch1 = 0;
...
delete pch1;

그럼 윈도우는 아래와 같은 오류보고 창을 띄운다. 오류 보고를 하면 MS로 오류보고 되지만 보내지 않을경우 log와 dump를 남기게 된다. dump 파일에 대한 이야기는 나중에 하고 오늘은 log를 보자.



아래 로그 파일을 보면...
"401867 이라는 주소에 오류->" 라고 표시한곳을 볼수 있다. 이곳이 에러(예외)가 발생한곳인데..
이 주소를 가지고 우리는 우선 2가지를 짐작할수 있다.
첫번째는 0x00400000 으로 시작되는 주소로 보아 EXE 파일(DLL은 0x10000000)이라는 점하고
두번째는 에러가 발생한 지점이 1867 이라는 곳이다.
그럼 소스코드에서 에러가 발생한 지점을 어떻게 알수 있을까?

로그 보기 펼치기..

빌드시 아래의 그림처럼 MAP 파일을 생성하자.
Project Options에 /mapinfo:lines 도 추가해준다.

그러면 우리는 아래와 같은 MAP 파일을 얻을수 있다.

Line numbers for .\Debug\dumptestDlg.obj(D:\project\dumptest\dumptestDlg.cpp) segment .text

  41 0001:000002f0    44 0001:00000322    53 0001:00000380    64 0001:000003b0

중간생략

  162 0001:000007e6   164 0001:000007ee   169 0001:00000810   170 0001:0000082d
  171 0001:00000833   175 0001:00000840   177 0001:0000085d   179 0001:00000864
  181 0001:0000086a
  183 0001:0000087c

401867 - 400000 - 1000 = 867 이라는 주소는 179 와 181 사이에 있다.
즉 코드의 179와 181 사이에서 에러가 발생했다는것을 알수가 있다.

실제로 해본결과 Project Options에 Optimizations(Maxmize speed /O2 Minimize Size / O1)  옵션을 추가했을경우 제대로된 log 파일을 얻을수 없었다.

Release 빌드시 위의 옵션 이외의 Default(/D) 옵션으로 해도 지장이 있을지 없을지는 아직 모르겠다. (Reversing이나 해킹방지차원에서)

참고서적 Debugging Applications for .NET and Windows - John robbins
그외에 유경상님 블러그, 정성태님홈피

2006/08/22 08:07 2006/08/22 08:07
Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • TWEETY 2006/08/22 09:57  댓글주소  수정/삭제  댓글쓰기
    저 책에 이런 내용이 있어요? ㅋㅋㅋ
    난 왜 못봤지? 너무 뜨문뜨문 봔나?

    난 지금 컴터가 맛가서 testPC에다가 연결해서 놀구 있어요.ㅜㅜ
    미치겐네.. 할 일이 산더민데... as기사는 언제올까..
  • 정성태 2006/08/22 10:20  댓글주소  수정/삭제  댓글쓰기
    "로그 보기" 의 링크가 ^^ 잘못된 것 같습니다.

    참고로, VS.NET 2005 (VC++ 8.0) 에서는 LINE INFO 를 생성하는 것이 아예 없어졌답니다. ^^
    • hongyver 2006/08/22 10:26  댓글주소  수정/삭제
      내용이 길어 more.../less... 버튼처리했습니다.
      누르면 아래로 쫘악...

      line info 생성이 없어졌다는건 옵션만 없어지고 디폴트로 생성된다는뜻인가요? 아님...
      아예 그런 기능이 없어졌다는 뜻인가요?

  • 증분링크
    2006/08/11 09:40

    증분링크(incremental link)는 바이너리를 가능한 빠르게 링크하고자 함수 호출시 직접적인 주소를 참조하지 않고 별도의 테이블(ILT, 증분링크테이블)로 관리를 하여 함수가 수정되어 크기가 변하여도 모든 CALL 명령을 찾아 수정을 가하는게 아니고 테이블만 변경할수 있도록 하여 링크 속도를 높인다.

    ILT 테이블 예
    @ILT+0(_wmain):
    00401005 jmp wmain(401070h)
    @ILT+5 (??_GCResString@@UAEPAXI@Z)
    0040100A jmp CResString::'scalar deleting destructor' (401B40h)

    증분링크시 추가적인 패딩데이타가 실행파일에 추가되어 용량이 커진다.

    /INCREMENTAL:yes 또는 no로 설정할수 있고 이와 관련된 정보는 .ilk에 저장되어 사용된다.

    디버그 모드에서 실행중 수정하면서 진행하려면 증분링크로 빌드되어야 한다. (VS에서는 /DEBUG 시 자동적으로 /INCREMENTAL 이 지정된다.)

    그외에 /OPT:REF /OPT:ICF 를 선택하면 증분링크는 무시되고 비증분링크를 수행한다.

    API Hooking시 증분링크에 대한 체크가 필요하다.

    // 증분 링크 체크
    // Microsoft Visual C++은 실행중에도 코드를 수정할 수 있게끔
    // 증분 링크를 사용하여 함수의 주소가 실제 함수를 가리키지 않고, 실제 함수로 jmp 하는 명령의 주소로 되어 있다.
    // 참고: System DLL일 경우에 무시
    if ((DWORD)fnApi < 0x80000000 && *(PBYTE)fnApi == 0xE9)
    {
      fnApi = (PROC)((int)(fnApi) + (*(int *)((PBYTE)fnApi+1)) + 5);
    }

    2006/08/11 09:40 2006/08/11 09:40
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • 윤제석 2006/08/11 23:42  댓글주소  수정/삭제  댓글쓰기
    홍선임님 그럼 해결책을 찾으셨내요!!

  • VS2005으로 작성한 코드가 다른 컴퓨터에서 작동을 안할때..
    2006/08/03 11:18
    이런 황당할때가...
    간단한 코드를 테스트하려고 VS.NET으로 작성한 MFC가 다른 VS.NET이 설치되지 않은 컴퓨터에서 작동하지 않는다.
    라이브러리를 복사해도 마찬가지고...

    응용 프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지...

    혹은 msvcr80.dll 를 찾을수 없다는 에러가 난다면...

    해결방법 :

    1. VS.NET용 라이브러리
    릴리즈 버전은
    C:\Program Files\Microsoft Visual Studio 8\VC\redist\x86
    디버그 버전은
    C:\Program Files\Microsoft Visual Studio 8\VC\redist\Debug_NonRedist
    에서 CRT, MFC, ATL에 맞는 dll을 실행파일과 같은 경로명에 복사해주면 된다.
    (Microsoft.VC80.*.manifest 도 포함해서)

    2. C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\vcredist_x86 또는

    http://www.microsoft.com/downloads/details.aspx?FamilyID=32bc1bee-a3f9-4c13-9c99-220b62a191ee&DisplayLang=ko

    자신에 맞는(x86등등) 것을 다운받아서 설치하면 된다.


    원인 :

    Windows XP에서는 DLL Hell[각주]새로운 프로그램을 설치할때 다른 프로그램이 사용하는 DLL을 덮어 쓰거나 혹은 버전의 불일치로 오는 문제로 기존의 프로그램들이 제대로 동작하지 않는 경우가 있다.[/각주]을 빠져나가려고 Side-by-Side Assembly라는 개념을 만들었다. 이건 여러 버전의 DLL을 동시에 시스템에 존재할 수 있게 해주고, DLL을 사용하는 응용프로그램이 원하는 DLL 버전을 골라서 쓸 수 있도록 해주는 방법이다.
    SideBySideAssembly 의 강력한 점은 응용프로그램에서 직,간접적으로 로드하는 모든 DLL이 이 설정의 영향을 받는다는 것이다. 이전에는 응용프로그램이 로드한 DLL에 의해서 간접적으로 로드되는 DLL을 제어할 수 있는 방법이 없었다.

    이러한 새로운 방식으로 기존방식대로 VS.NET 라이브러리를 복사하는것으로 끝나지 않고 배포를 위한 별도의 작업이 필요하게 된다.

    문제 해결에 대한 페이지
    http://www.serious-code.net/moin.cgi/RedistributingVisualCppRunTimeLibrary
    http://www.codeproject.com/cpp/vcredists_x86.asp
    http://cafe.naver.com/solidcode.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=8
    2006/08/03 11:18 2006/08/03 11:18
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • TWEETY 2006/08/04 09:09  댓글주소  수정/삭제  댓글쓰기
    VS 7.0부터 이런 일이 일어났던 거죠?
    괜히 7.0으로 개발했다가...
    한 참을 고생한 기억이 있어요.
    그 놈은 msvcr70.dll이 원인이었죠.
    찾아내는라 죽는 줄 알았음..-_-;;;
    PE?? 그런거 뒤져가지고 알아냈었어요. ㅠㅠ;
    • hongyver 2006/08/04 09:43  댓글주소  수정/삭제
      dll 이 문제가 아니라...
      CRT나 MFC,ATL의 배포라이브러러리를 같이 배포해도 예전같으면 같은 경로명에 있어도 실행이 당연 됐는데...
      그게 안되더라구...
      별도의 배포용 파일(vcredist_x86.exe)를 실행해주어야 한다는 사실...
      귀찮아 졌으...