JIT_NewCrossContext에서의 Exception Windows debugging

다음의 Callstack Exception Callstack 중의 하나인데, 언뜻 보면, HRESULT값이 80004003 (잘못된 포인터, System.NullReferenceException)임을 가늠할 있다. (굳이 0x06959590에 대한 DumpObject는 필요없을 수 도 ... )

 

ChildEBP RetAddr  Args to Child             

0d41f0d4 7923b543 e0434f4d 00000001 00000000 kernel32!RaiseException+0x53

0d41f12c 7923b4c6 06959590 00000000 0d41f388 mscorsvr!RaiseTheException+0xa0

0d41f154 7923b47a 06959590 00000000 0d41f398 mscorsvr!RealCOMPlusThrow+0x48

0d41f164 79239c8c 06959590 0000002b 00000000 mscorsvr!RealCOMPlusThrow+0xd

0d41f398 792b718e 0000002b 00000000 00000000 mscorsvr!CreateMethodExceptionObject+0x67b

0d41f42c 792b71b2 80004003 00000000 00000000 mscorsvr!RealCOMPlusThrowHRWorker+0xb9

0d41f448 792b731c 80004003 00000000 00000000 mscorsvr!RealCOMPlusThrowHRWorker+0x15

0d41f4d4 792a8552 80004003 00000000 0d41f514 mscorsvr!RealCOMPlusThrowHR+0x168

0d41f4e8 0268435d 00000000 00000000 80004003 mscorsvr!Interop::ThrowExceptionForHR+0x3a

WARNING: Frame IP not in any known module. Following frames may be wrong.

0d41f660 791f6049 00000001 0d41f69c 791f609d 0x268435d

0d41f66c 791f609d 0cafa2d8 0695800c 00000000 mscorsvr!CTPMethodTableCallTargetHelper+0xf

0d41f69c 791fa7f8 0cafa2d8 0695800c 00000000 mscorsvr!CTPMethodTable::CallTarget+0x4e

0d41f6c0 791b2682 0cb06e5c 00000000 00000001 mscorsvr!CRemotingServices::CreateProxyOrObject+0x5f

0d41f730 791b273c 0cb06e5c 0caff013 00000000 mscorsvr!AppDomain::GetOffsetOfSlotsCount+0xa0

0d41f74c 791b7f92 0d41f864 791bdd4e 0d41f7a0 mscorsvr!JIT_NewCrossContext+0x3a

0d41f754 791bdd4e 0d41f7a0 00000000 0d41f778 mscorsvr!CallDescrWorker+0x30

0d41f864 791d5bd5 00b068bb 026e8a10 024547a0 mscorsvr!MethodDesc::CallDescr+0x1b8

0d41f88c 79236659 0d41f8a8 024547a0 0d41fc8c mscorsvr!MethodDesc::Call+0x8e

0d41f8b8 792cec0a 06957e50 0d41f934 02683c9a mscorsvr!CallDefaultConstructor+0x10c

0d41f8c4 02683c9a 0d41f8d0 06957e50 0cafec57 mscorsvr!CRemotingServices::CallDefaultCtor+0xd

 

하나 중요한 사실은 Cross boundary Call임을 보여주는 , JIT_NewCrossContext , Enterprise Services Component 호출이 정상적으로 이뤄지지 않았음을 보여준다. 이는 분명 COM+ 등록된 Component 초기화에 실패했음을 역시 보여주고 있다. Manager Callstack 살펴보자.

 

0:029> !dumpstack -EE

Thread 29

Current frame:

  ChildEBP RetAddr    Caller,Callee

0d41f51c 0cafd1f5 (MethodDesc 0xcb03410 +0x255 System.EnterpriseServices.Thunk.Proxy.CoCreateObject)

0d41f548 0cafd107 (MethodDesc 0xcb03410 +0x167 System.EnterpriseServices.Thunk.Proxy.CoCreateObject)

0d41f5fc 0cafcd8d (MethodDesc 0xcb03890 +0x7d System.EnterpriseServices.ServicedComponentInfo.HasClassInterface2)

0d41f610 0cafa8a3 (MethodDesc 0xcb015c8 +0xb3 System.EnterpriseServices.ServicedComponentProxyAttribute.CreateInstance)

0d41f654 0cafa320 (MethodDesc 0x24fe618 +0x48 System.Runtime.Remoting.Activation.ActivationServices.IsCurrentContextOK)

0d41f6f0 0caff8f6 (MethodDesc 0xcb07600 +0x6e System.EnterpriseServices.RWHashTableEx.Put)

0d41f738 0caff013 (MethodDesc 0xcb01710 +0x2b AAA.BB.bbCOMPlus..ctor)

0d41f748 0caff1ee (MethodDesc 0xcb068c0 +0x6 AAA.BB.obj11..ctor)

0d41f8d0 0cafec57 (MethodDesc 0x21c9240 +0x7f System.Runtime.Remoting.Proxies.RealProxy.InitializeServerObject)

0d41f8f4 0cafec95 (MethodDesc 0x21c9240 +0xbd System.Runtime.Remoting.Proxies.RealProxy.InitializeServerObject)

0d41f934 0cafe683 (MethodDesc 0xcb02d28 +0x1b System.EnterpriseServices.ServicedComponentProxy.ConstructServer)

0d41f940 0cafe441 (MethodDesc 0xcb02bc8 +0x141 System.EnterpriseServices.ServicedComponentProxy..ctor)

0d41f96c 0cafe0f2 (MethodDesc 0xcb015f8 +0x8a System.EnterpriseServices.ServicedComponentProxyAttribute.System.Runtime.InteropServices.ICustomFactory.CreateInstance)

0d41f994 0cafdda3 (MethodDesc 0x24fe628 +0x4b System.Runtime.Remoting.Activation.ActivationServices.CreateObjectForCom)

0d41fc74 0cafdd24 (MethodDesc 0xcb034a0 +0x6c System.EnterpriseServices.Thunk.Proxy.CallFunction)

0d41fc98 0cafdd24 (MethodDesc 0xcb034a0 +0x6c System.EnterpriseServices.Thunk.Proxy.CallFunction)

0d41fcc0 0cafdc87 (MethodDesc 0xcb05a28 +0x67 System.EnterpriseServices.Internal.AppDomainHelper.System.EnterpriseServices.Internal.IAppDomainHelper.DoCallback)

 

일단, AAA.BB.bbCOMPlus..ctor Constructor에서 문제가 발생한 것을 보여주는 , return value 통해서 진행되고 있는 Assembly code 살펴볼 필요가 있다.

 

0:029> ub 0caff013

<Unloaded_mscorwks.dll>+0x22effd:

0cafeffd 47              inc     edi

0cafeffe 40              inc     eax

0cafefff 60              pushad

0caff000 f5              cmc

0caff001 8bce            mov     ecx,esi

0caff003 ff15d814b00c    call    dword ptr [<Unloaded_mscorwks.dll>+0x2314d8 (0cb014d8)]

0caff009 b95c6eb00c      mov     ecx,offset <Unloaded_mscorwks.dll>+0x236e5c (0cb06e5c)

0caff00e e8ef366b6c      call    mscorsvr!JIT_NewCrossContext (791b2702)

 

실질적으로 문제가 발생한 JIT_NewCrossContext 연관된 Class 찾는 것이 목적이다. 상위의 Assembly code 보면, JIT_NewCrossContext 호출하기 이전에 ecx register Keep 하는 뭔가가 있음을 확인할 있다. 이는 MethodTable 정보이다.

 

0:029> !dumpmt 0cb06e5c

EEClass : 0x0cb54568

Module : 0x026dc578

Name: xxxx.M.ConfigurationInit

mdToken: 0x02000011  (c:\winnt\assembly\gac\xxxx\1.2.1.4__0230c138d8d9aa19\xxxx.dll)

BaseSize: 0x28

MethodTable Flags : 0x20c0000

Number of IFaces in IFaceMap : 0x4

Interface Map : 0x0cb06f00

Slots in VTable : 30

 

그러므로, xxxx.M.ConfigurationInit class 초기화 실패로 인한 잘못된 포인터 오류가 발생했음을 있다.

 

사실 상위의 문제를 Fix하기 위해서는 추가적으로 해당 오류가 발생하기 직전에 발생한 1st chance exception 중에 해당 Class 연관 있는 exception check 해보는 것이 필요 하다. 아니면, 해당 class 초기화 실패가 코드의 원인일 수도 있기 때문에 code review로써 문제를 해결할 수도 있을 모른다.


Finalizer 쓰레드에 의한 메모리 누수 현상 Windows debugging

Finalizer Thread CLR 메모리 덤프에서 볼 수 있는 특이한 Thread 중에 하나이다. 오브젝트가 Garbage Collected 될 때, 오브젝트의 Finalize Method Finalizer에 의해서 수행이 된다. 일반적으로 이러한 Finalization이 필요한 오브젝트는 File 이나 Unmanaged Memory와 같은 Unmanaged Resource를 사용하는 오브젝트이며, 필요에 의해 Finalize Method를 구현하여 이와 같은 Unmanaged resource의 누수를 방지하는 역할을 하는 것이 Finalizer Thread이다. 일반적으로 Finalizer Thread를 확인하는 방법은 !threads의 출력으로 다음과 같이 확인할 수 있다.

 

0:026> !threads

ThreadCount: 12

UnstartedThread: 0

BackgroundThread: 12

PendingThread: 0

DeadThread: 0

                                  PreEmptive   GC Alloc                     Lock    

        ID  ThreadOBJ       State     GC       Context           Domain     Count APT Exception

 16  0xf44 0x000d23e0      0xa220 Enabled  0x06953ce0:0x06954ff4 0x00113b98     1 MTA

 20  0x7bc 0x000d3910      0xb220 Enabled  0x00000000:0x00000000 0x000cd460     0 MTA (Finalizer)

 22  0xf64 0x000ef538   0x1800220 Enabled  0x00000000:0x00000000 0x000cd460     0 MTA (Threadpool Worker)

 

그리고, 대부분의 메모리 덤프에서는 다음과 같은 Finalizer Thread Callstack을 확인할 수 있다.

 

0:020> kbL

ChildEBP RetAddr  Args to Child             

023ffdf8 7c967d19 7c82202c 00000002 023ffe48 ntdll!KiFastSystemCallRet

023ffdfc 7c82202c 00000002 023ffe48 00000001 ntdll!NtWaitForMultipleObjects+0xc

023ffea4 7c822fbe 00000002 793effc8 00000000 kernel32!WaitForMultipleObjectsEx+0x11a

023ffec0 7926ed87 00000002 793effc8 00000000 kernel32!WaitForMultipleObjects+0x18

023ffee0 792034b2 000002f0 00000000 7922ec48 mscorsvr!WaitForFinalizerEvent+0x5a

023fff24 7922ec3f 00000000 809a1724 7ff97000 mscorsvr!GCHeap::FinalizerThreadStart+0x96

023fffb8 7c82482f 000d3ad8 00000000 00000000 mscorsvr!Thread::intermediateThreadProc+0x44

023fffec 00000000 7922ebfe 000d3ad8 00000000 kernel32!BaseThreadStart+0x34

 

일반적으로 Finalizer Thread를 쳐다볼 일은 없을 것이다. 하지만, Finalizer ThreadBlocking으로 인해서 예상치 못한 메모리 누수현상이 발생할 수 있다. 쉽게 isolation 할 수 있는 것은
 

ü  Hang Dump를 몇 차례 수집하였을 때, !finalizequeue의 결과의 추이를 살펴보는 것일 것이다. 계속해서 Finalizequeue 오브젝트들이 쌓이고 있다면, Finalizer Thread Block을 의심할 수 있다  

ü  GC가 정상적으로 수행되지 않는 다고 생각될 경우에도 Finalizer Thread를 살펴보는 것이 좋을 것 같다. 일반적으로 메모리 누수의 원인이 되는 오브젝트들이 Reference를 갖고 있지 않는 경우라면, 다음 GC가 수행되었을 때, 메모리가 반환될 것이다. 하지만, 이러한 참조되지 않는 오브젝트가 대량으로 메모리를 차지 하고 있다면, GC의 수행이 그다지 Health한 상태가 아닐 수 있기 때문에 GC Thread와 더불어 Finalizer Thread 역시 확인 대상이 된다.

 

다음의 Thread Stack은 흥미롭다. Finalizer thread가 맞다. 하지만, EnterCriticalSection에서 Waiting을 하고 있다. 다음의 Managed Callstack 은 임의의 Finalize Method가 처리되고 있는 것이 눈에 띈다. 만일, 지속적으로 이와 같은 Callstack Stuck 되고 있다면, 이는 Finalizer Thread Block 된 상태로 보면 된다. 해당 Finalize Method와 연관된 CriticalSection Lock 또는 관련 Thread Stack Check해보는 것이 우선일 수 있다.

 

 0:002> kbL

ChildEBP RetAddr  Args to Child             

00c9f9fc 7c967d29 7c97d266 000011d8 00000000 ntdll!KiFastSystemCallRet

00c9fa00 7c97d266 000011d8 00000000 00000000 ntdll!NtWaitForSingleObject+0xc

00c9fa3c 7c97d2b1 000011d8 00000002 04b2b008 ntdll!RtlpWaitOnCriticalSection+0x1a3

00c9fa5c 608fa5af 08ddbf68 00c9fcc4 61ccc1ae ntdll!RtlEnterCriticalSection+0xa8

WARNING: Stack unwind information not available. Following frames may be wrong.

00c9fa68 61ccc1ae 006e2628 0487af7c 00000000 oracore10!sltsmna+0xf

00c9fcc4 61ccb998 08b60128 00000004 00000000 OraClient10!kpufhndl0+0x80e

00c9fcd8 61c2602c 08b60128 00000004 00c9fcf8 OraClient10!kpufhndl+0x10

00c9fce8 1000706c 08b60128 00000004 00c9fd7c OraClient10!OCIHandleFree+0x1a

00c9fd9c 7c966f69 00c9fe30 79215bdd 00c9fe20 oci!OCIHandleFree+0x18

00c9fda4 79215bdd 00c9fe20 7921d108 ffffffff ntdll!ZwDelayExecution+0xc

00c9fe30 79215ca5 0167b394 0015e1e0 0014c698 mscorwks!MethodTable::CallFinalizer+0xee

00c9fe44 791e0984 0167b394 7c822311 00000000 mscorwks!CallFinalizer+0x84

00c9fedc 791b997b 0167b394 00000000 00000000 mscorwks!FinalizeAllObjects+0xd5

00c9ff24 791c94b4 00000000 809a1724 7ffdb000 mscorwks!GCHeap::FinalizerThreadStart+0xc2

00c9ffb8 7c82482f 0015e398 00000000 00000000 mscorwks!Thread::intermediateThreadProc+0x44

00c9ffec 00000000 791c9473 0015e398 00000000 kernel32!BaseThreadStart+0x34

 

0:002> !dumpstack -EE

Thread 2

Current frame:

  ChildEBP RetAddr    Caller,Callee

00c9fd28 0393d63f (MethodDesc 0x320be10 +0xf System.Data.OracleClient.TracedNativeMethods.OCIHandleFree)

00c9fd34 0393bb47 (MethodDesc 0x320c1f8 +0xf7 System.Data.OracleClient.OciHandle.Dispose)

00c9fd7c 00879c75 (MethodDesc 0x320c188 +0x1d System.Data.OracleClient.OciHandle.Finalize)


Exception Object에 대해 살펴보기 Windows debugging

일반적으로 .NET Framework 기반의 Application에서 Exception이 발생하면, 이 역시 Exception Object의 형태로 Memory에 저장이 된다. 결국엔 kernel32!RaiseExceptione0434f4d 파라메터로 전송하게 되면, Debugger에서 CLR Exception으로 인식되는 데, 아래의 Call Stack CLR 2.0에서의 대표적인 모습이다. 그리고, mscorwks!RaiseTheExceptionInternalOnly 1번째 파라메터의 값은 어떤 Exception이 발생했는지, Exception Object에 대한 정보가 된다.

 

ChildEBP RetAddr  Args to Child             

0012ef08 79f97065 e0434f4d 00000001 00000001 kernel32!RaiseException+0x53

0012ef68 7a0945a4 0179a680 00000000 00000000 mscorwks!RaiseTheExceptionInternalOnly+0x226

*** WARNING: Unable to verify checksum for mscorlib.ni.dll

0012f02c 7952257c 0012f000 00000000 0179a610 mscorwks!JIT_Throw+0xd0

 

상위의 경우는 아래와 같은 Exception이 발생한 것이다.

 

0:000> .loadby sos mscorwks

0:000> !do 0179a680

Name: System.UnauthorizedAccessException

MethodTable: 7915fc6c

EEClass: 791eb60c

Size: 72(0x48) bytes

 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

790fa3e0  40000b5        4        System.String  0 instance 00000000 _className

79109208  40000b6        8 ...ection.MethodBase  0 instance 00000000 _exceptionMethod

790fa3e0  40000b7        c        System.String  0 instance 00000000 _exceptionMethodString

790fa3e0  40000b8       10        System.String  0 instance 017a23f4 _message

79113dfc  40000b9       14 ...tions.IDictionary  0 instance 00000000 _data

790fa9e8  40000ba       18     System.Exception  0 instance 00000000 _innerException

790fa3e0  40000bb       1c        System.String  0 instance 00000000 _helpURL

790f9c18  40000bc       20        System.Object  0 instance 00000000 _stackTrace

790fa3e0  40000bd       24        System.String  0 instance 00000000 _stackTraceString

790fa3e0  40000be       28        System.String  0 instance 00000000 _remoteStackTraceString

790fed1c  40000bf       34         System.Int32  1 instance        0 _remoteStackIndex

790f9c18  40000c0       2c        System.Object  0 instance 00000000 _dynamicMethods

790fed1c  40000c1       38         System.Int32  1 instance -2147024891 _HResult

790fa3e0  40000c2       30        System.String  0 instance 00000000 _source

790fe160  40000c3       3c        System.IntPtr  1 instance        0 _xptrs

790fed1c  40000c4       40         System.Int32  1 instance -532459699 _xcode

 

해당 Exception Object는 흥미로운 정보를 가지고 있다. 예를 들어, _HResult 값을 확인해 보면, 보기에 눈에 익숙하지 않지만, 다음과 같이 변환해 보면, 다소 우리에게 익숙한 HRESULT 값을 볼 수 있다.

 

0:000> ? -0n2147024891

Evaluate expression: -2147024891 = 80070005

 

, 해당 ExceptionAccess Denied Error와 관련 있음을 알 수 있다.

그리고, _xcode 를 살펴보면, 그냥 .NET Exception임을 보여준다.

 

0:000> ? -0n532459699

Evaluate expression: -532459699 = e0434f4d

 

또 다른 다음의 Exception의 예를 들어 보면,

 

0:000> !do 0c48dfa8

Name: System.NullReferenceException

MethodTable 0x03211a3c

EEClass 0x03201590

Size 64(0x40) bytes

GC Generation: 0

mdToken: 0x020000af  (c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll)

FieldDesc*: 0x00000000

        MT      Field     Offset                 Type       Attr      Value Name

0x008d575c 0x400001d      0x4                CLASS   instance 0x00000000 _className

0x008d575c 0x400001e      0x8                CLASS   instance 0x00000000 _exceptionMethod

0x008d575c 0x400001f      0xc                CLASS   instance 0x00000000 _exceptionMethodString

0x008d575c 0x4000020     0x10                CLASS   instance 0x01066ab8 _message

0x008d575c 0x4000021     0x14                CLASS   instance 0x00000000 _innerException

0x008d575c 0x4000022     0x18                CLASS   instance 0x00000000 _helpURL

0x008d575c 0x4000023     0x1c                CLASS   instance 0x0c48dfe8 _stackTrace

0x008d575c 0x4000024     0x20                CLASS   instance 0x00000000 _stackTraceString

0x008d575c 0x4000025     0x24                CLASS   instance 0x00000000 _remoteStackTraceString

0x008d575c 0x4000026     0x2c         System.Int32   instance 0 _remoteStackIndex

0x008d575c 0x4000027     0x30         System.Int32   instance -2147467261 _HResult

0x008d575c 0x4000028     0x28                CLASS   instance 0x00000000 _source

0x008d575c 0x4000029     0x34         System.Int32   instance 83683696 _xptrs

0x008d575c 0x400002a     0x38         System.Int32   instance -1073741819 _xcode

-----------------

Exception 0c48dfa8 in MT 03211a3c: System.NullReferenceException

_message: 개체 참조가 개체의 인스턴스로 설정되지 않았습니다.

_stackTrace:

04dece25 [DEFAULT] String ttt.ttt.ttt.1(SZArray Object)

04fcee50

00fbee70

00e3c34c [DEFAULT] [hasThis] Class System.Runtime.Remoting.Messaging.IMessage ttt.ttt.ttt.cls.Invoke(Class System.Runtime.Remoting.Messaging.IMessage)

04fcee6c

00fbee20

 

흥미롭게도 _stactTrace 라는 정보는 0x0c48dfe8 addressException이 발생한 시점의 stack dumping된 것으로 보면 된다. 그에 대한 정보가 상위의 Exception Object View 할 때 하단 부분에 첨부된 것을 볼 수 있기도 하다. 해당 Callstack Managed 이기 때문에 dds (또는 ddp) 명령은 아래와 같이 의미가 없을 수 있겠다.

 

0:000> dds 0x0c48dfe8

0c48dfe8  00c40274

0c48dfec  00000018

0c48dff0  04dece25

0c48dff4  04fcee50

0c48dff8  00fbee70

0c48dffc  00e3c34c

0c48e000  04fcee6c

0c48e004  00fbee20

0c48e008  00000000

 

하지만, 필요하다면, SOS 명령의 ip2md 를 이용해서 역시 문제 발생시의 Method 정보를 일일이 추출할 수 있다.

 

0:000> !ip2md 04dece25

MethodDesc: 0x00fbee70

Jitted by normal JIT

Method Name : [DEFAULT] String ttt.ttt.ttt.1(SZArray Object)

MethodTable 0xfbeeac

Module: 0x1c3980

mdToken: 0x060000b9 (c:\windows\assembly\gac\tttxxxx\2.1.0.0__de06f536f526478c\ttt.dll)

Flags : 0x10

Method VA : 0x04decd98

 

해당 Exception _xcode 정보를 보면,

 

0:000> ? -0n1073741819

Evaluate expression: -1073741819 = c0000005

 

이는 Access Violation 에 의한 Exception이 유발되었음을 알려준다. 보통 AV Exception record keep 하기도 하는 데, 이는 _xptrs 가 가지고 있곤 한다. 이는 kernel32!EXCEPTION_POINTERS 구조체를 가리킨다.

 

0:000> ? 0n83683696

Evaluate expression: 83683696 = 04fce970

 

0:000> dc 04fce970 l2

04fce970  00000018 00000000                    ........

 

상위의 Exception에서는 Exception Records 0x18 그리고, Context NULL 로 정상적으로 Keep 되지 않았지만, 해당 Context가 유용하다면, .cxr <context address> 을 통해 AV가 발생한 Context로 이동하여 Debugging할 수 있을 것이다. 

 


헷갈리는 COM+에서의 Transaction 속성 정리되지 않은 기술

Transaction이라는 것은 논리적으로 하나의 처리 sequence 갖고 임의의 process 처리하도록 하는 것을 의미하는데, 예를 들어, A, B, C 순서적으로 하나의 Transaction scope 안에 있다면, A, B, C 모두 성공해야만, 해당 process 처리 완료되는 것이고, 어느 하나라도 실패한다면, A, B, C 모두를 Rollback하도록 하는 것이 필요할 , Transaction 처리를 하게 된다.

COM+ Transaction 속성을 지정할 있는 , 다양한 Transaction 시나리오를 위해서 5가지의 Transaction 속성을 지정할 있다. 가지를 비교해서 설명하면,

 

Disabled Not supported 속성을 먼저 비교할 있다. 이들은 엄밀히 말하면 Transaction에서 벗어나 있다. 그러므로, 기존에 해당 Component 호출하는 이전에 이미 Transaction 존재한다면, Not supported 경우는 해당 Transaction context 하위의 Component 흘려 보내지 않는 . 하지만, Disabled 이미 존재하는 Transaction Context 무관하지만, 하위의 Component Transaction Context 흘러 보냄으로써 Transaction Context Share한다. 하지만, 이들 해당 Component에서는 Transaction 무관하므로 Transaction Complete/Abort 대한 voting 없다.

 

오히려 Disabled Supported 경우는 동일하게 Transaction Context Share하는 경우로, 이미 존재하는 Transaction 하위 Component 흘려 보낼 있다. Disabled 자신은 Transaction Context 존재와 상관없이 transaction 무관하다면, Supported Transaction Context 존재할 경우에 Transaction 있으며, 존재하지 않으며, Transaction 타지 않고 수행된다. 그러므로, Disabled 와는 달리 Transaction 대한 Voting 가능하다.

 

Required Required New 비교해보자. Transaction 반드시 타는 경우이므로, Transaction 대한 Voting 가능/필요하다. 하지만, Required 기존에 Transaction 이미 존재한다면, 해당 transaction 타지만, 어떠한 transaction 존재하지 않는 다면, 스스로 Root transaction , New Transaction 만든다. 하지만, Required New 경우는 기존의 Transaction 존재 여부와 상관없이 항상 Root transaction 된다. COM+ Nested Transaction 지원하지 않기 때문에 새로운 Transaction으로 분기되는 경우가 생긴다면, 각각의 Transaction 별개의 Transaction으로 간섭하지 않는 다는 것도 명심해야겠다.

 

만일, Transactional 속성이 Disabled Component에서 Exception 발생하였다면?

상위 내용대로라면, 해당 Component에서의 SetAbort 기존의 transaction 영향을 주지 않는 것이 맞을 것이다. 하지만, Exception Propagation 하여 Transactional Component(Disabled Not supported 아닌)안에서 명시적으로 SetAbort 것인지, SetComplete 것인지를 결정할 있겠다. 복잡한 것이 문제가 Disabled Component 이전의 Transactional Component뿐만이 아니라 Exception handling여부에 따라 미치게 되는 Disabled Component 하위에 transactional Component 존재할 있기 때문에 정상적인 Exception handling외에 .하위 Component Transactional Voting역시 프로그램 흐름상 고려되어 필요가 있을 지도 모르겠다.


GC가 시작되는 순간에 Hang 현상 Windows debugging

High CPU현상이 아닌 상황에서 hang Dump 수집했는데, Lock 존재할 것이라고 생각했다.

 

0:100> !locks

 

CritSec +14ce60 at 0014ce60

WaiterWoken        No

LockCount          134

RecursionCount     1

OwningThread       162c

EntryCount         0

ContentionCount    86

*** Locked

 

CritSec +15d558 at 0015d558

WaiterWoken        No

LockCount          2

RecursionCount     1

OwningThread       8a4

EntryCount         0

ContentionCount    2

*** Locked

 

2개의 Lock 보이므로, 이것들이 서로서로 deadlock 유발하겠구나 하고 쉽게 생각했지만, 그렇지 않았다. 일단, 134개의 lock 잡고 있는 162c Thread GC Thread였다.

 

0:100> ~~[162c]s

eax=00000001 ebx=04fce8c0 ecx=002381e8 edx=7c96ed64 esi=04fce8c4 edi=7ffd4000

eip=7c96ed64 esp=04fce874 ebp=04fce91c iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!KiFastSystemCallRet:

7c96ed64 c3              ret

0:014> kb

ChildEBP RetAddr  Args to Child             

04fce870 7c962104 7c82716b 00000002 04fce8c0 ntdll!KiFastSystemCallRet

04fce874 7c82716b 00000002 04fce8c0 00000001 ntdll!NtWaitForMultipleObjects+0xc

04fce91c 7c821095 00000002 04fcea5c 00000000 kernel32!WaitForMultipleObjectsEx+0x11a

04fce938 79248be3 00000002 04fcea5c 00000000 kernel32!WaitForMultipleObjects+0x18

04fceb70 791e0723 00000002 00000000 00000000 mscorwks!Thread::SysSuspendForGC+0x248

04fceb88 792144f9 00000001 00000000 00000000 mscorwks!GCHeap::SuspendEE+0xcf

04fceba4 7924199e 00000000 00000000 00000e44 mscorwks!GCHeap::GarbageCollectGeneration+0x103

04fcebd4 791b3868 00238a08 00000e44 00000000 mscorwks!gc_heap::allocate_more_space+0x13a

04fcedf8 791b300a 00238a08 00000e44 00000000 mscorwks!GCHeap::Alloc+0x5f

04fcee0c 791dd118 00000e44 00000000 00000000 mscorwks!Alloc+0x3a

04fcee2c 791dd0b9 00bd0088 0000071c 00000000 mscorwks!FastAllocatePrimitiveArray+0x45

04fceeac 00bc1336 00000000 0000071c 0c67ab58 mscorwks!JIT_NewArr1+0xbb

WARNING: Frame IP not in any known module. Following frames may be wrong.

00000000 00000000 00000000 00000000 00000000 0xbc1336

 

이것은 아마도 Memory Allocation중에 적정한 Size Memory 할당받지 못했나 보다. 하여, GC 수행되기 위해 EE Suspend 하고 있다. 만일, Thread 진행되지 않는 다면, 이는 Suspend 되지 않는 Thread 존재할 것이라 믿는 . 전에 대부분의 Thread 아래와 같은 Thread 동일한 형태였다.

 

0:014> ~100s

eax=000019d6 ebx=00000000 ecx=00000000 edx=630d5cb8 esi=0014ce60 edi=000005b8

eip=7c96ed64 esp=10c0f9bc ebp=10c0f9f8 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!KiFastSystemCallRet:

7c96ed64 c3              ret

0:100> kb

ChildEBP RetAddr  Args to Child             

10c0f9b8 7c962114 7c97972f 000005b8 00000000 ntdll!KiFastSystemCallRet

10c0f9bc 7c97972f 000005b8 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc

10c0f9f8 7c979640 00000000 00000004 0000000a ntdll!RtlpWaitOnCriticalSection+0x19c

10c0fa18 791b6ffc 0014ce60 10c0fa4c 79268960 ntdll!RtlEnterCriticalSection+0xa8

10c0fa24 79268960 0014ce60 00000000 0fdc43c8 mscorwks!EE_EnterCriticalSection+0xc

10c0fa38 791b48f8 7c828ce5 0fdc43c8 7c82bccd mscorwks!BaseCrst::Enter+0x8a

10c0fa4c 791d09e8 00000000 00000001 0fdc43c8 mscorwks!ThreadStore::LockThreadStore+0x9d

10c0fa60 791d0abb 0fdc43c8 00000000 7c82bbcd mscorwks!ThreadStore::AddThread+0xd

10c0fa88 7929c0de 0020de28 10c0fb48 7931712e mscorwks!SetupThread+0x103

10c0fa94 7931712e 00000000 0020de28 0020de28 mscorwks!SetupThreadPoolThread+0x15

10c0fb48 79317611 0017cd48 00000001 00000001 mscorwks!AddTimerCallbackEx+0x16

10c0fb5c 792eebc6 0017cd48 00000001 792eebb7 mscorwks!AddTimerCallback+0x10

10c0fb70 792ee6ca 0020de28 0512fe38 793fa170 mscorwks!ThreadpoolMgr::AsyncTimerCallbackCompletion+0xf

10c0fb84 792ee8c3 0512fe38 00000000 792ee79a mscorwks!ThreadpoolMgr::ExecuteWorkRequest+0x19

10c0fba4 792edfdb 00000000 00000014 10c0fbdc mscorwks!ThreadpoolMgr::WorkerThreadStart+0x129

10c0ffb8 7c8260b9 001a8488 00000000 00000000 mscorwks!ThreadpoolMgr::intermediateThreadProc+0x44

10c0ffec 00000000 792edf97 001a8488 00000000 kernel32!BaseThreadStart+0x34

0:100> dc 0014ce60

0014ce60  0014cf80 fffffde6 00000001 0000162c  ............,...

0014ce70  000005b8 00000000 0014cfa8 ffffffff  ................

0014ce80  00000000 00000000 00000000 00000000  ................

0014ce90  0015d610 0014ce90 00238a40 00000008  ........@.#.....

0014cea0  00000000 00000006 00000000 00000001  ................

0014ceb0  00000000 00000000 00000000 00000000  ................

0014cec0  00000000 002389d0 0000162c 00000111  ......#.,.......

0014ced0  00100010 000c01b6 003a0063 0077005c  ........c.:.\.w.

 

ntdll!RtlEnterCriticalSection Function 우리가 흔히 알고 있는 EnterCriticalSection 함수와 유사하다면, 첫번째 파라메터는 Critical Section Object 것이다. Binary 살펴보면, 0xc offset 162c 라는 값이 들어 온다. 이미 살펴 본봐 이는 !lock에서 보여주었던 GC Thread ThreadID 동일하다. 아마도, 해당 Critical Section Owner 보여주는 것으로 보인다.

 

결국, GC Thread Suspend Wait 하고 있는 이유를 밝히는 것이 중요하다. 아쉽게도 !lock에서 보여준 2번째 Thread Owner Orphan Thread임이 밝혀졌다.

 

0:100> ~~[8a4]d

             ^ Illegal thread error in '~~[8a4]d'

0:100> ~ <<< ALL thread 살펴볼 있으나, ThreadID 8a4 놈을 찾을 없다.

 

그리고, 중요한 것은 다음.

 

0:100> !threads

Loaded Son of Strike data table version 5 from "C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll"

ThreadCount: 8

UnstartedThread: 0

BackgroundThread: 6

PendingThread: 0

DeadThread: 1

                                  PreEmptive   GC Alloc                     Lock    

        ID  ThreadOBJ       State     GC       Context           Domain     Count APT Exception

  0 0x6928 0x0015d5a0      0xa020 Enabled  0x00000000:0x00000000 0x0014c130     0 MTA

  2  0x86c 0x00155508      0xb220 Enabled  0x00000000:0x00000000 0x0014c130     0 MTA (Finalizer)

  3  0x750 0x00194cf0      0x1220 Enabled  0x0c484a6c:0x0c486140 0x0014c130     0 Ukn

  5 0x70fc 0x001820d8   0x2001220 Enabled  0x00000000:0x00000000 0x0014c130     0 Ukn

 10 0x51f8 0x001a6c48    0x800222 Disabled 0x0c3a7c60:0x0c3a7c70 0x0014c130     0 MTA (Threadpool Completion Port)

XXX      0 0x001bbfc0    0x800820 Enabled  0x00000000:0x00000000 0x0014c130     1 MTA (Threadpool Completion Port)

 13 0x63e8 0x002381e8    0x800222 Disabled 0x0c489bf0:0x0c48a140 0x0014c130     0 MTA (Threadpool Completion Port)

 14 0x162c 0x002389d0    0x800220 Disabled 0x0c6ae3d8:0x0c6af044 0x0014c130     1 MTA (GC) (Threadpool Completion Port)

 

보면, PreEmptive GC Enabled 되지 않은 Thread GC Thread 포함해서 3개가 존재한다. 14 Thread GC Thread 다른 모든 Thread Suspend 때까지 기다릴 터이다. 그럼, 10, 13 Thread Disabled 인지 확인할 필요가 있다.

 

0:100> ~10s

eax=00003a24 ebx=00000000 ecx=00000000 edx=5dc101c9 esi=0015d558 edi=00000498

eip=7c96ed64 esp=0488df7c ebp=0488dfb8 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!KiFastSystemCallRet:

7c96ed64 c3              ret

0:010> kb

ChildEBP RetAddr  Args to Child              

0488df78 7c962114 7c97972f 00000498 00000000 ntdll!KiFastSystemCallRet

0488df7c 7c97972f 00000498 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc

0488dfb8 7c979640 00000000 00000004 0000000a ntdll!RtlpWaitOnCriticalSection+0x19c

0488dfd8 791b6ffc 0015d558 0488e008 79268960 ntdll!RtlEnterCriticalSection+0xa8

0488dfe4 79268960 0015d558 0488e15c 0488e15c mscorwks!EE_EnterCriticalSection+0xc

0488dff8 79254d88 0488e15c 0488e140 0488e044 mscorwks!BaseCrst::Enter+0x8a

0488e008 7921d09b 0488e140 0488e6b0 0488e15c mscorwks!CPFH_HandleManagedFault+0x61

0488e044 7921d135 0488e140 0488e6b0 0488e15c mscorwks!GetPrevSEHRecord+0x703

0488e05c 7c96eeca 0488e140 0488e6b0 0488e15c mscorwks!COMPlusFrameHandler+0x3d

0488e080 7c96ee9b 0488e140 0488e6b0 0488e15c ntdll!ExecuteHandler2+0x26

0488e128 7c96ecd6 04888000 0488e15c 0488e140 ntdll!ExecuteHandler+0x24

0488e128 04dece25 04888000 0488e15c 0488e140 ntdll!KiUserExceptionDispatcher+0xe

WARNING: Frame IP not in any known module. Following frames may be wrong.

0488e424 0488e650 0c3a7468 0c3a7504 0c3a4af8 0x4dece25

00000000 00000000 00000000 00000000 00000000 0x488e650

 

눈에 띄는 것은 KiUserExceptionDispatcher . (http://byung.egloos.com/4710539) 이것은 Exception 발생한 경우이다. 해당 함수의 Stack 살펴보면, 아래와 같은 몇몇 정보를 얻을 있다.

 

0:010> dc 0488e128

0488e128  0c3a7ac8 7c96ecd6 04888000 0488e15c  .z:....|....\...

0488e138  0488e140 0488e15c c0000005 00000000  @...\...........  <<< c0000005: Access Violation

0488e148  00000000 04dece25 00000002 00000000  ....%........... <<< 04dece25: Exception Address

0488e158  00000000 0001003f 00000000 00000000  ....?...........  <<< 1003f Context Flags

0488e168  00000000 00000000 00000000 00000000  ................

0488e178  ffff027f ffff4020 ffffffff 00bc4c08  .... @.......L..

0488e188  00ca001b 0488e6d8 ffff0023 00000000  ........#.......

0488e198  00000000 00000000 00000000 00000000  ................

 

결국, 해당 Thread 04dece25 에서 Access Violation 대한 Exception으로 인해 GC 의해 Suspend 되지 않은 것으로 보인다. 이는 Orphan Thread 관련이 있을 수도 있다. 임의의 Orphan Thread (Thread ID:8a4) Critical Section Lock 잡고 Terminated 되었기 때문에 해당 Exception Thread에서 Critical Section Lock 걸려있을 수도 있다. 그리고, 추가적으로 13 Thread 역시 10 Thread 동일한 Exception Thread이다.

 

0:013> kb

ChildEBP RetAddr  Args to Child             

00d4e5f0 7c962114 7c97972f 00000498 00000000 ntdll!KiFastSystemCallRet

00d4e5f4 7c97972f 00000498 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc

00d4e630 7c979640 00000000 00000004 0000000a ntdll!RtlpWaitOnCriticalSection+0x19c

00d4e650 791b6ffc 0015d558 00d4e680 79268960 ntdll!RtlEnterCriticalSection+0xa8

00d4e65c 79268960 0015d558 00d4e7d4 00d4e7d4 mscorwks!EE_EnterCriticalSection+0xc

00d4e670 79254d88 00d4e7d4 00d4e7b8 00d4e6bc mscorwks!BaseCrst::Enter+0x8a

00d4e680 7921d09b 00d4e7b8 00d4ed28 00d4e7d4 mscorwks!CPFH_HandleManagedFault+0x61

00d4e6bc 7921d135 00d4e7b8 00d4ed28 00d4e7d4 mscorwks!GetPrevSEHRecord+0x703

00d4e6d4 7c96eeca 00d4e7b8 00d4ed28 00d4e7d4 mscorwks!COMPlusFrameHandler+0x3d

00d4e6f8 7c96ee9b 00d4e7b8 00d4ed28 00d4e7d4 ntdll!ExecuteHandler2+0x26

00d4e7a0 7c96ecd6 00d4d000 00d4e7d4 00d4e7b8 ntdll!ExecuteHandler+0x24

00d4e7a0 04dece25 00d4d000 00d4e7d4 00d4e7b8 ntdll!KiUserExceptionDispatcher+0xe

WARNING: Frame IP not in any known module. Following frames may be wrong.

00d4ecc8 00e3b502 00d4ed10 00fbcda8 00d4ed28 0x4dece25

00000000 00000000 00000000 00000000 00000000 0xe3b502

 

0:013> dc 00d4e7a0

00d4e7a0  00d4ecc8 7c96ecd6 00d4d000 00d4e7d4  .......|........

00d4e7b0  00d4e7b8 00d4e7d4 c0000005 00000000  ................

00d4e7c0  00000000 04dece25 00000002 00000000  ....%...........  << Exception Address 10 Thread 동일한 위치!!

00d4e7d0  00000000 0001003f 00000000 00000000  ....?...........

00d4e7e0  00000000 00000000 00000000 00000000  ................

00d4e7f0  ffff027f ffff0000 ffffffff 00bc4c08  .............L..

00d4e800  00ca001b 00d4ecd8 ffff0023 00000000  ........#.......

00d4e810  00000000 00000000 00000000 00000000  ................

 

그러므로, Orphan Thread 역시 이와 같은 Exception 문제가 되는 것은 아닌 의심해 본다. 결국, 해당 GC suspend Hang Exception 대한 Fix 우선적으로 처리되는 것이 필요하며, Orphan Thread 위해 1st chance exception 대한 추가적인 메모리 덤프를 분석해 필요도 있을 모른다.


추가적으로
Exception Address Assembly code !u (해당 Thread managed thread stack 보여줬다.) 통해 확인해 필요가 있겠다. 여기서 또한, .cxr 이용하여 (Context Flags address pointer 이용) Context switch 하여 Exception 발생 Thread Stack으로 이동할 필요도 있을 것이다. 이하 생략한다. 단지, 해당 Process hang 상태를 확인하는 과정으로써 Thread Stack Pattern이나 분석 과정을 흩어보는 것으로 마무리 한다.

 


1 2 3 4 5 6 7 8 9 10 다음