|
Message 기반의 Window에서 특히 MFC Application 같은, Debugging 시에 특정 Button을 Click 하면 Button Click에 대한 Function Call이 호출되고 어쩌구저쩌구 하는 경우, 간혹, 정확한 정보를 모르는 상태에서 Debugging을 하다 보면 난감할 때가 있다. 이런 경우, 가장 유용한 방법이 SPY Tool을 이용하는 것이다. 예를 들어, button하나 누르는 MFC Application을 가지고 살펴보면, 아래는 SPY로 main window를 잡은 화면이다. 지금 하고자 하는 것은 button click하는 함수를 찾아 들어가는 것이다. (굳이 왜 이런 짓을 ..
SPY TOOL을 열어놓고, 메뉴의 Search 에서 Find Window 하여 MFC Application의 Main window에 drag하면 Windows1 화면에서 Highlighted 된 Window가 바로 그놈 이다. 이름이 MfcBtnTest 라는 놈이다. 그리고, View 메뉴에서 속성을 확인하면 Property Inspector라는 창이 뜬다. 원하는 정보는 WinProc 이다. 여기에 5804f141 이라는 Address가 보인다. 이게 뭘까 …. WinDbg로 해당 Application에 걸면 WinProc Function이 보일 테이다. 아래와 같다. 아시다시피 AfxWndProcBase 이다. MFC Application의 기본적인 WinProc 이 되겠다. 보통 일반적인 Window Application 이라면, WindowProc (http://msdn.microsoft.com/en-us/library/ms633573(VS.85).aspx) 일 텐데.. 0:001> ln 5b04f141 f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxstate.cpp(439) ß아시다시피 해당 소스는 VS에서 구할 수 있다. C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src (5b04f141) mfc90u!AfxWndProcBase | (5b04f1c1) mfc90u!AfxGetAppModuleState Exact matches: mfc90u!AfxWndProcBase (struct HWND__ *, unsigned int, unsigned int, long) 조금은 복잡하게 됐다. 그리고, button click에 대한 Function을 쫓아 갈 차례다. WindowProc이건 AfcWndProcBase 건 간에 Message ID로 주고 받는 다. 이것도 역시 SPY로 정보를 얻을 수 있다. Main Window에서 Message Filtering을 하는 데, WM_COMMAND 를 잡으면 된다.
wID 는 Msg 정보를 나타낸다. 1000(0x3e8) 이것이 Message 정보이다. 그러므로, Message Handler로 0x3e8 이 처리되는 것이 바로 내가 원하는 Button Click 시의 Message 를 나타낸다. 간단하게는 WindowProc의 Assembly Code에서 0x3e8을 Handling 하는 놈을 찾으면 되겠다. MFC에서는 _AfxDispatchCmdMsg 함수에서 0x3e8(두 번째 parameter)을 Handling 하여 원하는 Function으로 보낸다. 0:000> kp ChildEBP RetAddr 001af54c 5b080346 mfc90u!_AfxDispatchCmdMsg(class CCmdTarget * pTarget = 0x001afc20, unsigned int nID = 0x3e8, int nCode = 1766796, <function> * pfn = 0x01031440, void * pExtra = 0x00000000, unsigned int nSig = 0, struct AFX_CMDHANDLERINFO * pHandlerInfo = 0x00000000) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp @ 54] 001af568 5b070e92 mfc90u!CCmdTarget::OnCmdMsg(unsigned int nID = 0x3e8, int nCode = 0, void * pExtra = 0x00000000, struct AFX_CMDHANDLERINFO * pHandlerInfo = 0x00000000)+0x124 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp @ 381] . . . 0:000> ln 0x01031440 g:\codes\mfcbtntest\mfcbtntest\mfcbtntestdlg.cpp(154) (01031440) MfcBtnTest!CMfcBtnTestDlg::OnBnClickedButton1 | (01031460) MfcBtnTest!CDialog::Create Exact matches: MfcBtnTest!CMfcBtnTestDlg::OnBnClickedButton1 (void) 0:000> uf 0x01031440 MfcBtnTest!CMfcBtnTestDlg::OnBnClickedButton1 [g:\codes\mfcbtntest\mfcbtntest\mfcbtntestdlg.cpp @ 154]: 154 01031440 6a00 push 0 156 01031442 68e0350301 push offset MfcBtnTest!`string' (010335e0) 156 01031447 68e0350301 push offset MfcBtnTest!`string' (010335e0) 156 0103144c 6a00 push 0 156 0103144e ff15b8300301 call dword ptr [MfcBtnTest!_imp__MessageBoxW (010330b8)] 157 01031454 c3 ret 아 왜 이런 쓸데없는 짓을 그냥 OnBnClickButton1에 Break을 걸면 되지 않나.. 머 이럴수도 있겠다만, 맞는 말이다. 하지만, 여러 3rd party application을 Debugging 하다 보면, 복잡한 UI에서 도대체 Button을 Click 하면 어떤 Function으로 들어가는지 아무도 모르는 경우가 존재한다. 또 그런 Application을 Debugging 할 기회(?)도 존재한다. 그와 같은 때 유용하며, 이렇게 SPY와 WinDBG를 통해서 Message를 쫓아가는 경우는 여러 가지 응용도 가능하겠다. Application Localization은 특히 대한민국이나 중국, 일본 등과 같은 나라들에게 민감한 사항이 아닐 수가 없는 데, .NET Framework 기반하의 개발, 특히 WPF Application 개발 시 Localization Guidance 에 대한 참고할 만한 문서가 codeplex에 publishing 되었다. WPF Localization Guidance - Release: WPF Localization Guidance Whitepaper 링크: http://wpflocalization.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=29389 .NET 개발하다가 웬 xxxxx.resources file 이 .. 했던 분들에게 강추. 지식이란 것이 무엇인가. 가식적이거나 비굴하게 행동하는 것은 내 이성적 감성적 판단에 가장 최선의 선택이라고 여겨지는 것에 대한 행동일 수도 있다. 모든 행위에는 동기가 있다. 내가 내 의지에 따라 행동하는 것은 그 시점에 그렇게 행동 해야 한다는 당위성이 의식적 무의식적으로 뒷받침이 되었기 때문에 나오는 것이다. 사실 특별한 운동을 배우면서 같은 동작을 끊임없이 반복하는 것은 무의식 중에서라도 그러한 상황이 오면 그렇게 행동하라는 것을 각인시키는 것이다. 물론, 그러한 상황이 왔을 때 의식적으로 다르게 행동할 수 있지만, 몸으로 익힌 습관이란 것은 무섭다. 지식을 머리에 쌓는 것도 이러한 운동을 배우는 것과 같다. 특정한 상황에서 이성적으로 반드시 필요한 행동은 머리 속에 남아있는 지식의 판단에 의해 좌지우지하는 것일지 모른다. 그러므로, 가식적으로 또는 비굴하게 보이는 행동도 삶에 있어서 본인이 지각하지 않는 의도된 행동일 수 있다. 그러한 행동으로 인해 비참함을 느끼거나 후회하는 것도 물론 있을 수 있다. 왜 그래야만 했나. 그렇게 행동하지 않으면 안되었을 까. 물론, 모든 행동에 최선으로 임하는 것은 쉬운 일이 아니다. 그게 바로 완벽하다는 것이다. 이성적으로 완벽한 매 순간의 최선. 이건 불가능하다. 지하로부터의 수기에서 지식인의 비굴함 및 지식을 토대로 한 가식이 느껴진다. 책을 읽는 당사자인 나로써는 주인공이 지적으로 또는 객관적으로 뛰어남을 기대했을 지도 모르다. 주인공이 얼마나 지적인지 판단되지 않지만, 생각과 행동에 다소 이질적인 느낌은 실망으로 다가온다. 하지만, 우리 세계에선 그게 자연스러운 거다. 성경을 보면, 예수님의 논리적인 말솜씨나 초현실적인 능력은 초인적이다. 그럼에도 불구하고 인자라는 점이다. 누구나 예수님을 본받길 원할 수 있다. 하지만, 얼마나 많은 이가 이와 같은 완벽한 인간으로 살아갈 수 있다고 보나. 초현실적인 능력을 배제하고도 말이다. 나는 부족한 인간이 비난 받을 가치는 없다고 생각한다. 스스로 비굴하고 가식적이고 등등의 느낌을 받는 순간이 있다면, 이에 대해 너무 비참해할 필요는 없다. 지하로부터의 수기에 나오는 지하인처럼 얻을 수 없는 무언가를 위해 노력하는 삶을 포기하는 방황은 필요 없다. 가진 지식의 선택에 따른 자연스러운 행위는 다분히 인간적이라고 본다. Garbage Collector(GC)는 .Net Framework / CLR 에서 Memory를 관리해주는 Module 이다. 그러므로, 개발자의 의도와 상관없이 적절한 시점에 GC가 수행하고, 그것도 비동기적으로 발생하며, 그에 의해 메모리를 효율적으로 사용할 수 있도록 해준다. 문제는 GC가 비동기적으로 수행됨에 의해 임의의 시점에 GC의 수행과 더불어 응용프로그램의 성능에 영향을 줄 수도 있다라는 것이다. 성능에 영향을 미친다는 것은 예상보다 긴 시간 동안에 GC가 수행되고 이에 의해 High CPU상태가 User가 견디기 힘들 정도로 지속될 수 도 있다는 것이다. 그렇다면, .NET Application이 High CPU일 경우에 GC에 의해 영향을 받은 것인지 어떻게 체크할 수 있을 까. 사실 해당 작업은 그렇게 쉽게 확인할 수 없지만, 몇 가지 요소를 통해서 문제를 좁혀나갈 수 있다. 첫 번째로는 %Time IN GC/.NET CLR Memory 성능 카운터의 값을 확인하는 것이다. 이 값은 최근 GC가 수행함에 의해 소요된 시간의 백분율 값이다. 설명은 그렇게 되어 있는 데, 일단, 50% 이상으로 높다라면, Managed Heap에서 Allocation 되는 패턴을 확인해야 한다. 일반적으로 평균 5% 정도나 10%이하라면 신경 쓸 필요가 없어 보인다. 그리고, 5% 이하라면 의미 있는 Data가 아니므로, 어떠한 판단을 하기가 어렵다. 하지만, 50% 이상이라면, Allocated Bytes/sec 를 확인하여 Allocation rate 를 확인하는 것이 필요하다. 실제로, Performance Monitoring을 통해서 % Processor Time이 High CPU상태로 어느 정도 지속이 된다면, 문제라고 판단한다. 그러므로, High CPU를 유발하는 Thread를 Check 하고, Thread Stack을 Check하여 Looping과 같은 Code에 의해 Thread가 High CPU를 유발하는 것이 아닌가 확인하는 작업이 필요하게 되는 데, 해당 Thread가 역시 GC Thread일 수도 있다. 이는 Memory Dump에서 성능로그에 High CPU를 유지하는 Thread ID를 Check하여 해당 Thread ID에 대한 메모리 덤프의 Thread를 Check했을 때, SOS extension에서 제공하는 !threads 의 result 중 GC라고 표시된 Thread와 일치한다면, GC에 의해서 High CPU라고 확인할 수 있을 것이다. 또는 %Time IN GC의 값이 변동이 있을 때, 예를 들어, High에서 Low로 떨어지거나 Low에서 High로 상승하는 시점에 %Processor Time이 Peak를 친다면, GC에 의해서 High CPU가 유발되었다고 추측할 수도 있다. 이는 메모리 덤프에 의한 Double check가 필요하다. % Time IN GC가 높아서 High CPU라는 것은 결국, GC가 너무 자주 수행되거나 오랫동안 수행된다는 것이다. 그러므로, 이미 언급한 것처럼, 일반적인 Allocation rate이 높아서 GC가 자주 발생하는 것이라면, Allocation pattern에 대한 체크가 필요하며, 두 번째는 # GenX GC counters를 확인하는 것이다. 이는 얼마나 자주 GC가 수행되는 지 확인할 수 있을 것이다. 일반적으로 초점이 되는 것은 Gen2 GC이다. Gen0/Gen1은 Performance에 크게 영향을 주지 않는 다. 하지만, Gen2나 Large Object Heap의 경우는 Performance에 영향을 많이 준다. 일반적으로 gen2:gen1 의 비율이 1:10+ 정도라면 크게 우려할 정도가 아니다. 하지만, 그렇지 않다면, gen2에서의 GC 수행이 영향을 미쳤다고 추측하게 된다. 이 경우는 응용프로그램이 gen2에 적재되어 있을 수 있고, 계속해서 promotion이 gen1으으부터 이뤄지고 있다고 판단할 수 있는 데, 결국, gen2까지 살아남은 Objects에 대한 Verification이 필요하다. Gen2까지 살아 남을 필요가 있는 Objects들인가.. 이는 메모리 덤프에서 SOS !dumpheap –stat 및 –gen2 command를 통해서 Objects를 확인할 수 있다. 그러므로, 응용프로그램의 Performance를 위해서 Objects의 조기에 release 하는 Code적인 장치가 필요할 수도 있다. 결국, 이러한 경우에는 개발자가 성능적인 이유를 위해서 적절한 Objects release 를 간여해야 한다는 것이다. GC만을 믿을 수 없다. 마지막으로는 모호하긴 하지만, #induced GC값이다. 해당 값은 프로세스가 시작된 이후에 GC의 발생횟수를 나타낸다. 이 값이 너무 큰 값이라면, 이는 응용프로그램의 버그를 의심할 수 있다. 가끔 메모리 힙의 크기가 증가되는 것을 줄이기 위해서 강제적으로 GC.Collect 를 호출하는 경우가 있는 데, 이로 인하여 오히려 performance 에 문제가 된 것은 아닌지 확인해 볼 수도 있을 것 같으며, 그렇지 않으면, Heap이 Corruption 발생하여 비 정상적인 동작이 있을 수도 있다. 이 경우도 High CPU가 될 수 있음을 확인해야 할 것이다 사실 아직까지 VB runtime이 Internet Explorer에 올라가 있다는 것은 그리 놀랍지 않다. COM개발이 도입이 되면서 ActiveX Control의 개발은 VB가 대체로 대세였다. 개발의 속도 면에서 그리고, User Interface를 쉽게 제공해주는 면에서 거부할 수 없는 선택 이였다. 하지만, VB runtime의 태생적인 Threading Model의 한계는 Internet Explorer가 마치 Server Application Environment 처럼 변모하면서 너무나 많은 제약을 얻을 수 밖에 없게 되었다. 다음의 Crash 현상 역시 이러한 변화 속에서 드러나는 단면이다. 0:000> kbL ChildEBP RetAddr Args to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 001273e8 66107873 0eddbd00 00000000 00127494 <Unloaded_es_kr.dll>+0x64ff81 00127400 66107bec 00000000 00127424 03ffe070 MSVBVM60!ExHandleLBFailure+0x20 00127458 661079e5 03ffe070 80010003 00000004 MSVBVM60!ExLateIdNamed+0xca 00127484 66108b9a 03ffe070 80010003 fffffffd MSVBVM60!ExLateIdSt+0x4a 0012749c 06977430 03ffe070 80010003 00000004 MSVBVM60!__vbaLateIdSt+0x16 00127508 660ca914 0cd4f1d0 00000000 0cd4f1d0 ttt!DllCanUnloadNow+0x2e61e 00127564 660cae57 00000006 00000001 0e801210 MSVBVM60!RUN_INSTMGR::ExecuteInitTerm+0x178 00127598 660caab3 00000000 00000013 0400758c MSVBVM60!RUN_INSTMGR::CreateObjInstanceWithParts+0x1e4 00127618 660cbc3a 0cd4f1ec 00000000 00127a00 MSVBVM60!RUN_INSTMGR::CreateObjInstance+0x14d 001278cc 66072b6e 001278e4 00000000 03ffa66d MSVBVM60!TipCreateInstanceEx+0xd5 00127908 66078642 01572100 06930000 00000000 MSVBVM60!CThreadPool::CreateInstanceFactory+0xfd 00127a04 66078226 03ff4e8c 00000000 00000000 MSVBVM60!CPrivClassFactory::CreateInstanceLic+0x16e 00127a20 7e605619 03ff4e8c 00000000 7e3a3028 MSVBVM60!CPrivClassFactory::CreateInstance+0x1a 00127a5c 7e60aa10 031d8e48 031d9654 80004005 mshtml!COleSite::InstantiateObjectFromCF+0x114 00129ae0 7e60b386 7e3b919c 03ff4e8c 031d9654 mshtml!COleSite::CreateObjectNow+0xa5 00129b04 7e60bc1a 031d9610 7e3b919c 03ff4e8c mshtml!CCodeLoad::OnObjectAvailable+0x84 00129b78 7e60bf76 7e391b04 00129c68 031d8e00 mshtml!CCodeLoad::BindToObject+0x460 00129b98 7e605369 031d8e00 00129c68 7e60f904 mshtml!CCodeLoad::Init+0x287 00129c18 7e60f85c 00129c68 13ca5100 031d9470 mshtml!COleSite::CreateObject+0x26d 0012ddc8 7e5f393b 7e4306e9 13c9b6c0 00000020 mshtml!CObjectElement::CreateObject+0x72a es_kr.dll 이 unload되었다고 믿기 어렵다. 최종 Stack에서 Assembly를 Check하더라고 이것이 정확한 instruction인지 믿기 어려운 상황이다. 0:000> u <Unloaded_es_kr.dll>+0x64ff81: 00650072 6f outs dx,dword ptr [esi] 00650073 bc46c045c0 mov esp,0C045C046h 00650078 0000 add byte ptr [eax],al 0065007a 0000 add byte ptr [eax],al 0065007c e8e8448a00 call <Unloaded_es_kr.dll>+0xef4478 (00ef4569) 00650081 0000 add byte ptr [eax],al 00650083 0000 add byte ptr [eax],al 00650085 008001000000 add byte ptr [eax+1],al 이러한 경우는 최종적으로 Thread Stack이 정상적으로 보이는 Frame에서 EIP가 잘못될 가능성이 없는 지를 Check 해야 한다. ChildEBP RetAddr Args to Child 001273e8 66107873 0eddbd00 00000000 00127494 <Unloaded_es_kr.dll>+0x64ff81 00127400 66107bec 00000000 00127424 03ffe070 MSVBVM60!ExHandleLBFailure+0x20 0:000> ub 66107873 MSVBVM60!ExHandleLBFailure+0xc: 6610785f ff15b8100066 call dword ptr [MSVBVM60!_imp__TlsGetValue (660010b8)] 66107865 8bd8 mov ebx,eax <--- eax 는 TlsGetValue의 return 값 66107867 8b03 mov eax,dword ptr [ebx] 66107869 85c0 test eax,eax 6610786b 7409 je MSVBVM60!ExHandleLBFailure+0x23 (66107876) 6610786d 8b08 mov ecx,dword ptr [eax] <--- ecx 는 eax address 값 6610786f 50 push eax 66107870 ff5108 call dword ptr [ecx+8] 0:000> r eax=0eddbd00 ebx=001e1f38 ecx=0ce1ef38 edx=04009b10 esi=00127494 edi=00000000 eip=00650072 esp=001273ec ebp=00127400 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00240206 <Unloaded_es_kr.dll>+0x64ff81: 00650072 6f outs dx,dword ptr [esi] ds:0023:00127494=fffffffd 0:000> ? 0ce1ef38+8 Evaluate expression: 216133440 = 0ce1ef40 <--- call dword ptr[0ce1ef40] 하지만, 아래 data를 보면, Method로 보이진 않는 다. 단지 String Data로 보인다. 그러므로, Tls 가 Broken 된 것으로 추측할 수 있다. 0:000> dc 0ce1ef40 0ce1ef40 00650072 003a0072 00680020 00740074 r.e.r.:. .h.t.t. 0ce1ef50 003a0070 002f002f 00300037 0032002e p.:././.7.0...2. 0ce1ef60 0031002e 00300034 0031002e 00300034 ..1.4.0...1.4.0. 0ce1ef70 0039003a 00310030 002f0036 00740068 :.9.0.1.6./.h.t. 0ce1ef80 006c006d 00700043 002f0063 00720070 m.l.C.p.c./.p.r. 0ce1ef90 002f006f 0048004d 0046005f 00310030 o./.M.H._.F.0.1. 0ce1efa0 0030005f 002e0031 0073006a 000d0070 _.0.1...j.s.p... 0ce1efb0 0041000a 00630063 00700065 002d0074 ..A.c.c.e.p.t.-. 0:000> dc 0ce1ef40-10 0ce1ef30 00a20016 030a01ee 0e98bb18 00650066 ............f.e. 0ce1ef40 00650072 003a0072 00680020 00740074 r.e.r.:. .h.t.t. 0ce1ef50 003a0070 002f002f 00300037 0032002e p.:././.7.0...2. 0ce1ef60 0031002e 00300034 0031002e 00300034 ..1.4.0...1.4.0. 0ce1ef70 0039003a 00310030 002f0036 00740068 :.9.0.1.6./.h.t. 0ce1ef80 006c006d 00700043 002f0063 00720070 m.l.C.p.c./.p.r. 0ce1ef90 002f006f 0048004d 0046005f 00310030 o./.M.H._.F.0.1. 0ce1efa0 0030005f 002e0031 0073006a 000d0070 _.0.1...j.s.p... 0:000> u MSVBVM60!ExHandleLBFailure 66107873 MSVBVM60!ExHandleLBFailure: 66107853 55 push ebp 66107854 8bec mov ebp,esp 66107856 53 push ebx 66107857 56 push esi 66107858 57 push edi 66107859 ff357cee1066 push dword ptr [MSVBVM60!g_itlsEbthread (6610ee7c)] 6610785f ff15b8100066 call dword ptr [MSVBVM60!_imp__TlsGetValue (660010b8)] 66107865 8bd8 mov ebx,eax 66107867 8b03 mov eax,dword ptr [ebx] 66107869 85c0 test eax,eax 6610786b 7409 je MSVBVM60!ExHandleLBFailure+0x23 (66107876) 6610786d 8b08 mov ecx,dword ptr [eax] 6610786f 50 push eax 66107870 ff5108 call dword ptr [ecx+8] Thread Stack은 다르더라도 상위의 TLS(Thread Local Storage)연산에 대한 Assembly code는 기억해둘 필요가 있다. VB runtime은 Thread Safe 한 처리를 이유로 주로 TLS를 이용한다. 하지만, IE7 이상의 Browser에서 TAB Browser의 처리에 따라서 동일한 VB ActiveX Control이 중복해서 하나의 Browser에 load되는 가능성도 커졌다. 이에 따라서 TLS slot이 고갈되어 VB Runtime의 TLS 연산에서 crash가 발생하는 경우가 빈번해졌다. 만일, Vista OS 사용한다면, 같이 shipping된 VB runtime에서는 문제되지 않지만, XP machine이라면 이러한 Crash를 볼 수 있다. 최근에 Internet Explorer와 같은 Multiple Threaded 환경에서 ActiveX Control로써 VB 를 사용하는 경우는 확실히 줄었지만, 과거의 Control을 유지 보수하는 차원에서 존재하는 것이라면, VB Runtime에서의 Crash는 당황될 수 있다. 가장 많이 보고 되는 것이 1) vb6ko.dll 이라는 모듈이 System32 Directory에 존재하지 않아서 Crash가 발생하는 경우와 2)TLS Operation에서의 VB runtime에서의 Crash이다. (설마 아직도 IE/VB Control을 Single Threading으로 사용하여 Crash되는 경우가 있을 거라고는 ... 생각치 않는다.) TLS Operation에서의 VB runtime Crash의 경우는 쉽게 Vista OS에 shipping된 VB Runtime은 XP Box에 copy하여 배포하는 경우도 있고, KB930828에서 제공하는 VB runtime으로 Update 하는 방법이 존재한다. 이 경우는 Microsoft 기술지원부를 Contact해야 하는 번거로움이 존재한다. 드라마 남자이야기에서 채도우가 만들려고 하는 네오모나코는 부르주아의 상징인 듯. 그렇게 소시민을 천민이라 경멸하는 태도는 마냥 과장되어 보이지 만은 않는다. 김신의 연인이었다가 채도우의 연인으로 변하는 서경아라는 인물은 적어도 구조적으로 인간사회가 계급적인 사회라는 것을 인지한 인물이다. 사는 물이 다르다 라는 언급은 적어도 김신이 드라마 초기에 이해하지 못한 부분이라는 것. 이것은 로또에 당첨되면 이제 세상 편하게 살겠다라는 소시민의 생각이 부질없다는 것을 보여주는 단면이자 소시민이 로또에 당첨되어 선망이 되는 비싼 아파트를 샀다. 그래서.. 뭐… 이렇게 되는 거다. 서경아가 김신에게 그토록 설명하지만 김신이 깨닫지 못하는 것. 사실 김신을 눈뜨게 한 것은 양시장이란 부조리적 인간. 부조리 안에서 살아가는 부조리적 인간. 감정적으로 분노하거나 포기하거나 하지 않고 부조리 속에서 부조리를 바라보면서 이성적으로 반항하는 인간. 아쉽게도 어떤 미친놈에 의해 희생이 되지만 적어도 또 다른 부조리적 반항적 인간을 양산하는 … 머.. 그런 건가. 드라마는 오늘 끝이 나지만, 아… 그러저러해서 부조리한 인간들이 모두 행복해지는 소시민적 혁명으로 막을 내린다. 그렇진 않을 거다. 역사가 그렇질 않은 가. 어제만 해도 또 다른 부르주아 세력이 모든 것을 쓸어 담는 듯 보이는 데. 작가의일기를 보면 실존주의에 그렇게 영향을 주었던 포도르 도스토엡스키가 유럽에 대한 러시아의 열등의식을 나타내면서 유럽의 왕권을 허문 후에 정권을 잡은 유럽 전반적인 부르주아에 대한 썩 좋지 않은 감정이 보인다. 그에 대비하여, 러시아 전반에 대한 농민의 깨우침에 대한 강조가 있을 뿐, 물리적인 혁명을 추구하는 것으로 보이진 않는 데… 그래서 그런가. 그리스도정교에 의한 평등적 평화적 이상주의를 꿈꿨던 것은 아닌지. 남자이야기는 적어도 평등적 평화적 이상주의로 가진 않을 듯 하다. 채도우의 동생, 아 이름이 생각나질 않는다.. 채은수 이 모든 소시민적 사회악을 교화시키지 않는 한. 단지 부조리한 인물들만 있을 뿐. 명확하다. 부조리한 인물로 반항적인 삶을 살거나, (종교적인 의미를 떠나서 라도)기독교적 이상적인 삶의 태도를 갖거나. Managed Code에서 "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." 와 같은 오류가 발생하는 것은 흔하지 않다. 이것은 Native Code에서 잘못된 메모리를 참조하는 경우에 발생하는 데, 아시다시피 Managed환경에서는 포인터를 이용한 직접적인 메모리를 다루는 경우가 거의 없기 때문에 경험할 일이 없을 것이다. 하지만, PInvoke 와 같은 Interop 를 사용하는 경우라면 달라질 수 있다. 이는 결국 Native COM을 Managed 에서 사용하는 것이므로, 여전히 Native COM에서의 문제가 Managed환경으로 전달되었을 때, Access Violation의 위험성이 있는 것이다. 결국, Pageheap을 enable 하거나 해서 Crash Dump를 다시 잡는 것이 필요할 수도 있다. 일단, Memory Dump를 확인하면 문제가 발생한 Callstack에 아래와 같은 System.AccessViolationException을 볼 수 있다. 0:131> !dso OS Thread Id: 0xacc (131) RSP/REG Object Name 0000000027bda3f0 00000008d0fff488 System.Threading.Thread 0000000027bda438 00000008d0fff488 System.Threading.Thread 0000000027bda588 00000009710807f8 System.String 0000000027bda5a8 00000009710807f8 System.String 0000000027bda5b0 0000000971080908 System.String 0000000027bda668 0000000971080908 System.String 0000000027bda7d0 00000008d100adc8 System.String 0000000027bda7f8 00000008d100ca20 System.String 0000000027bda840 00000008d100b930 System.String 0000000027bda900 00000008d100b930 System.String 0000000027bda9c0 00000008d0fff7c0 System.String 0000000027bda9c8 00000008d0fff7c0 System.String 0000000027bdaaf0 00000008d10095f8 System.String 0000000027bdab08 00000008d100ca20 System.String 0000000027bdab68 00000008d100ca20 System.String 0000000027bdab80 00000008d0fff340 System.AccessViolationException 0000000027bdabe0 00000008d0fff340 System.AccessViolationException 0000000027bdad28 00000008d0fff340 System.AccessViolationException 0000000027bdb0f0 00000008d0ffd308 System.String 0000000027bdb108 00000008d0ffd290 System.String 0000000027bdb110 00000008d0ffd2b0 System.String 0000000027bdb138 00000008d0ffd270 System.String 0000000027bdb140 00000008d0ffd240 System.String 0000000027bdb148 00000008d0ffd208 System.String 0000000027bdb680 00000008d0fff340 System.AccessViolationException 0000000027bdb910 00000008d0fff340 System.AccessViolationException 0000000027bdb938 00000008d0fff340 System.AccessViolationException . . . 그리고, 먼저, !verifyheap 을 통해서 Managed Heap을 Check 하는 것도 필요하다. 0:131> !verifyheap -verify will only produce output if there are errors in the heap ------------------------------ Heap 0 object 0000000880ff00d0: does not have valid MT curr_object : 0000000880ff00d0 Last good object: 0000000880ff00b0 ---------------- total 4 objects 출력된 결과는 curr_object가 corruption되었다는 것과 메모리 영역에서 가장 가까운 Object중에 정상적인 Object의 address를 알려준다. 그러므로, curr_object 를 살펴보면 invalid 하다는 결과를 얻는다. 0:131> !do 0000000880ff00d0 <Note: this object has an invalid CLASS field> Invalid object 0:131> !do 0000000880ff00b0 Name: System.String MethodTable: 00000642784365a0 EEClass: 000006427803e4f0 Size: 32(0x20) bytes GC Generation: 0 (C:\WINDOWS\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) String: AES Fields: MT Field Offset Type VT Attr Value Name 0000000000000000 4000096 8 System.Int32 1 instance 4 m_arrayLength 0000000000000000 4000097 c System.Int32 1 instance 3 m_stringLength 0000000000000000 4000098 10 System.Char 1 instance 41 m_firstChar 0000000000000000 4000099 20 System.String 0 shared static Empty >> Domain:Value 00000000d75a2aa0:NotInit Unable to get AppDomain d7900cc0 0000000000000000 400009a 28 SZARRAY 0 shared static WhitespaceChars >> Domain:Value 00000000d75a2aa0:NotInit Unable to get AppDomain d7900cc0 정상적인 Data를 같이 보여주는 것은 GC에서는 Memory Allocation이 순차적으로 일어난다는 것과 무관하지 않다. low address에서 아래와 같이 Check 하여 Data를 살펴보거나 해당 Object의 MethodTable 정보를 Check 하는 것은 개발자가 관련있는 Code를 verify 하는 데 도움이 될 수 있기 때문이다. 0:131> dc 0000000880ff00b0 00000008`80ff00b0 784365a0 00000642 00000004 00000003 .eCxB........... <--- valid Object 00000008`80ff00c0 00450041 00000053 00000000 00000000 A.E.S........... 00000008`80ff00d0 7843e5e0 00000642 0000000d 00000000 ..CxB........... <--- invalid Object 00000008`80ff00e0 32313338 30323031 ................. 또한 !gcroot 에 대한 결과로 관련된 Object 등을 Check 할 수 있는 데, 아래의 경우는 조금 실망스러운 결과이다. 0:131> !gcroot 0000000880ff00b0 Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info. Scan Thread 52 OSThread 1440 Scan Thread 79 OSThread f4c Scan Thread 145 OSThread 1f64 Scan Thread 149 OSThread 144c Scan Thread 99 OSThread 18e4 Scan Thread 148 OSThread 1d38 Scan Thread 93 OSThread 1c0c Scan Thread 91 OSThread 149c Scan Thread 146 OSThread 1980 Scan Thread 96 OSThread 1dac Scan Thread 136 OSThread f84 Scan Thread 98 OSThread 6ac RSP:33c6cbb8:Root: 0000000880ff00b0(System.String) RSP:33c6cd88:Root: 0000000880ff00b0(System.String) RSP:33c6ce28:Root: 0000000880ff00b0(System.String) RSP:33c6cfc0:Root: 0000000880ff00b0(System.String) Scan Thread 106 OSThread 1a10 Scan Thread 108 OSThread 1ce4 Scan Thread 122 OSThread 190c Scan Thread 121 OSThread 1698 Scan Thread 110 OSThread 14a8 Scan Thread 107 OSThread 1c10 Scan Thread 116 OSThread 1b80 Scan Thread 117 OSThread 141c Scan Thread 131 OSThread acc Scan Thread 135 OSThread 6e8 Scan Thread 126 OSThread 1d1c Scan Thread 132 OSThread 1e18 해당 Object가 존재하는 98번 Thread에서 연관된 Callstack을 Check 할 수 있다. 하지만, Native에서도 마찮가지로 Heap Corruption이 발생한 것은 Dumping 시점 이전에 문제가 발생한 것이 이후에 해당 Managed Heap의 사용에 의해 AV가 발생한 경우가 많기 때문에 문제가 정확히 드러나지 않을 수 있다. 그러므로, 다소 시간을 걸리더라도 Pageheap을 enable 하고 여러 차례 Dump를 수집할 필요가 있을 지도 모른다. 그리고, 1st chance AV 및 기타 Exception에 대해서도 Callstack의 Check 라던가 관련성 여부를 살펴보는 것이 필요할 수 있다. 일단, 부조리와 함께하는 삶은 거부할 수 없을 듯 하다. 부조리란 일반적으로 “상식으로 이해할 수 없다” 라는 말로 대표된다. 사실 많은 것들이 증명되었다. 그럼에도 불구하고 이 세상이 힉스로 차있건 에테르로 차있건 간에 끊임없이 새로운 부조리가 생산되는 듯하다. 이는 물리적인 것만을 이야기하는 것은 아니다. 비 물리적인 도덕적인 관점에서도 마찮가지이다. 아직까지는, 글쎄, 범인이 이 세상의 시작과 끝에 대한 선험적인 이해를 구하기 힘들고, 시간의 체계를 벗어나 시공을 바라보는 것에 대한 생각도 이해하기 힘들다. 항상 개념적으로 이해하고 논리적으로 생각하는 것에 대한 한계는 이 부조리한 세계에서 대단히 자유롭지 못하다. 만일, 스타트렉 영화에 나오는 스팍과 같은 논리 체계의 종족만이 존재한다면 그 세계는 이상적인 세계 인가. 그것은 모든 것이 선험적으로 증명된 세계, 획일화된 태도, 양심을 들출 필요가 없는 기계적인 삶이고 그 안에서 살아가는 개체들이 존재하는 것이다. 그것들은 자유가 없다. 곰곰히 생각해 보지 않아서 그렇지 선험적으로 이것은 가능하지 않다는 것을 이미 알고 있다. 그리고 도덕적인 부조리는 물리적인 부조리와 더불어 존재한다. 그러므로 부조리의 존재를 인정하면 부조리한 벽에 부딪혔을 때, 아마도 약간의 동요, 감정적인 동요, 나의 의지와 상관없는 인간적인 분출, 그리고 그 다음에 내가 할 수 있는 행동들. 예상할 수 있다. 이것은 기계적으로, 논리적으로, 이런 상황에선 이렇게 행한다를 결정하는 것과는 다소 차이가 있다. 기계적으로 행하는 것은 부조리에 대한 무시이다. 이는 삶에서의 자유 의지가 없는 죽은 상태이다. 그러므로, 부조리를 뼈속깊이 인식하고 질적 또는 양적, 내가 소유하고 누릴 수 있는 것, 내가 바라보는 자연, 숨쉴 수 있는 공기, 을 내 자유의지에 따라 누리는 것, 이거다.
|
카테고리
이글루 링크
최근 등록된 덧글
감사합니다. 잘 읽었습..
by Lai Go at 06/24 아 17번 어케함? 17번에서 .. by Dragon at 06/14 전혀 문제되지 않습니다... by 강세윤 at 06/13 좋은 정보 감사드립니다... by 배용민 at 06/12 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ.. by 이륜관 at 05/29 21번안드로넣는거에여 by assddda at 05/03 애라못하겟다!!!!!!!!!!!!!!!!!!!!!.. by xxczx at 04/19 pdb file 입니다. by 강세윤 at 04/15 c:\MySymbols 에 넣.. by kuaaan at 04/15 아 유용한 정보네요. 감.. by eslife at 04/15 이글루 파인더
| |||