멀티프로그래밍 위키로 바로가기 → http://www.devnote.net/wiki

오늘 TSF (Text Service Framework)를 지원하는 Application을 개발하는데 있어, 디버깅 하는데  큰 문제가 있음을 알아 냈습니다.

먼저 자세한 설명을 하기 전에 증상부터 알아 봅시다. 아래 소스는 Vista SDK에 TSF 예제로 포함된, TSFAPP의 TEXTSTOR.CPP의 일부입니다. InsertTextAtSelection 함수는 새로운 글자가 입력될 때 마다 호출되는 함수입니다. 함수 시작 부분에 메모리 주소 0 에 쓰기를 시도하여 Access Violation을 유발 시켜보았습니다. 이것은 심각한 버그가 있는 것을 시뮬레이션 하는 것입니다.


/**************************************************************************

  CTSFEditWnd::InsertTextAtSelection()

**************************************************************************/

STDMETHODIMP CTSFEditWnd::InsertTextAtSelection(    DWORD dwFlags,
                                                   const WCHAR *pwszText,
                                                   ULONG cch,
                                                   LONG *pacpStart,
                                                   LONG *pacpEnd,
                                                   TS_TEXTCHANGE *pChange)
{
  OutputDebugString(TEXT("CTSFEditWnd::InsertTextAtSelection \n"));

  *(int *) 0 = 0;  // <-- Access Violation 유발


이제 TSFAPP.EXE를 컴파일시켜, 한글을 입력해 봅시다.  그런데,  예상과 달리 이 어플리케이션은 크래시되지도 않고 우리가 흔히 보는 윈도우즈 에러 리포트 다이얼로그도 나타나지 않습니다. 단지 원하는 한글 입력은  되지 않습니다. 이것이 왜 디버깅에 문제가 되는 지, 아마 어느 정도 짐작하였을 것입니다. 어플리케이션이 제공하는 TSF 인터페이스 내부에, 아무리 심각한 버그가 있어도 프로그램이 죽거나 에러를 리포트하지 않는다는 것입니다.  이유는 무엇일까요? 다름 아닌 윈도우즈의 TSF입력 지원 모듈이 자체 예외처리기를 가지고 있기 때문입니다.

따라서, 디버거를 처음에 attach시켜 프로그램을 실행하지 않는 한, access violation과 같은 심각한  버그가 있어도, 어느 순간 입력에 이상이 생기는 현상만 나타나며, 원인을 알아내기 어렵다는 점 입니다. 특히 어플리케이션이 자체 예외처리기(SEH)를 가지고 콜스택을 로그파일로 만들거나 덤프파일을 만드는 것이 불가능해 집니다. 따라서, 어플리케이션 버그로 인한 입력 문제가 발생함에도 불구하고 사용자나 개발자는 OS에 문제가 있는 것으로 오인할 가능성도 많습니다.

윈도우 TSF 모듈이 자체 예외처리기를 가지는 것을 확인하기 위해서, 우선 위에서 만든 TSFAPP.EXE를 실행해 봅시다. 그리고 VS에서 Attach to Process를 선택하거나, 이 프로세스의 ID를 알아낸 다음, "ntsd -p <프로세스 ID>"와 같이 커맨드라인 디버거를 이용하여 디버거를 attch 합니다. 이제 한글을 입력하려고 하면 디버거에서 이 메모리 오류(Access Violation)을 캐치함을 알 수 있습니다. 이것은 First chance exception 입니다. 여기서 다시 계속 진행시키기 위해 디버거에서 "go"를 선택하면, 아래와 같은 콜스택을 볼 수 있습니다.

TSF 핵심 모듈인 MSCTF.DLL이 자체 exception handler를 가지고 있는 것을 알 수 있다. 여기서 다시 "go"를 하면 프로그램은 종료되지 않고 계속 실행된다.

여기서 CicExceptionFilter가 EXCEPTION_EXECUTE_HANDLER 값을 리턴하는 것을 짐작할 수 있습니다. 즉, MSCTF 자체 예외처리기에 걸리면 다른 SEH는 이것을 알지 못합니다.  내가 아무리 최고의 unhandled exception handler를 가지고 있다 해도 무용지물입니다. 여기서 한가지 해결책은 XP이후부터 지원되는 Vectored Exception Handler를 사용하는 것입니다.


0012f434 77999b31 ntdll!DbgBreakPoint
0012f46c 77999b94 ntdll!RtlUnhandledExceptionFilter2+0x2a4
0012f47c 767ec8f5 ntdll!RtlUnhandledExceptionFilter+0x12
0012f488 767dc902 MSCTF!CicExceptionFilter+0xe
0012f490 7672669b MSCTF!CInputContext::_DoEditSession+0x49
0012f4a4 76726620 msvcrt!_EH4_CallFilterFunc+0x12
0012f4d0 76812a3b msvcrt!_except_handler4_common+0x8e
0012f4f0 77951039 MSCTF!_except_handler4+0x20
0012f514 7795100b ntdll!ExecuteHandler2+0x26
0012f5bc 77950e97 ntdll!ExecuteHandler+0x24
0012f5bc 00402a26 ntdll!KiUserExceptionDispatcher+0xf
0012f8d0 76803991 TSFApp!CTSFEditWnd::InsertTextAtSelection+0x6
0012f90c 767ee809 MSCTF!CACPWrap::InsertTextAtSelection+0x38

0:000> uf MSCTF!CicExceptionFilter
MSCTF!CicExceptionFilter:
77bbc8e7 mov     edi,edi
77bbc8e9 push    ebp
77bbc8ea mov     ebp,esp
77bbc8ec push    dword ptr [ebp+8]
77bbc8ef call    dword ptr [MSCTF!_imp__RtlUnhandledExceptionFilter (77b811d8)]
77bbc8f5 xor     eax,eax
77bbc8f7 inc     eax => retrun
EXCEPTION_EXECUTE_HANDLER
77bbc8f8 pop     ebp
77bbc8f9 ret     4




그런데, 왜 MSCTF는 Access Violation과 같은 심각한 예외상황을 무시하고 지나가도록 한 것일까? MSCTF가 모든 경우 자체 예외 처리를 통해 이런 심각한 예외를 무시하도록 하고 있지는 않다. 간단한 예로 InsertTextAtSelection가 아닌 다른 함수들 안에서 잘못된 메모리 참조하면 정상적으로 프로그램이 종료되고, 윈도우즈 오류 리포팅과 같은 Post Mortem Debugger가 실행된다. 이유는 좀 황당하지만 이렇다.

MS 내부에서 TSF를 지원하는 어플리케이션과 입력기들이 많이 개발되었고, 어플리케이션 혹은 입력기들의 자체 버그로 인해 중요 어플리케이션이 종료되는 현상이 자주 나타나자, TSF 팀은 많이 사용되는 Callback 함수들을 아예 자체 예외처리기로 묶어서 심각한 문제가 발생해도 어플리케이션을 죽이지 않고, 현재 입력만을 취소한 채로 계속 수행되도록 만든 것이다.

특히 TSF의 내부 구조의 복잡성으로 인해, 사소하고 미묘한 문제가 어플리케이션을 죽이는 상황으로 쉽게 니티나기 시작하였다. TSF 팀은 이들 입력기를 개발한 팀에 수정을 요구하기 보다는 자체적으로 이러한 버그를 피해가는 방법을 선택하였다. (많은 종류의 어플리케이션들이 TSF를 잘못 사용하여 문제를 일으켰기 때문일 것이다)

어플리케이션이 죽지 않아 입력된 데이터의 손실은 막을 수 있겠으나, 이로인한 부작용은 앞서 설명한 바와 같이 TSF를 사용하는 어플리케이션이나 입력기의 심각한 문제를 발견하기 어렵게 되었을 뿐 아니라, 입력 중인 글자가 취소되거나 한영 변환이 되지 않는 등 가끔 매우 이상한 현상이 감지될 수 있다.

TSF 어플리케이션을 제작할 때 입력에 문제가 발생한다면, 디버거를 attach시키고 실행하여야만 access violation과 같은 심각한 버그가 숨어 있음을 알 수 있다.



크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback Address :: http://devnote.net/trackback/43


◀ PREV : [1] : ... [54] : [55] : [56] : [57] : [58] : [59] : [60] : [61] : [62] : ... [93] : NEXT ▶