14

Abbiamo un problema con la nostra applicazione che utilizza una combinazione di codice gestito (C#) e non gestito (C++). Fondamentalmente abbiamo un exe che richiama un gruppo di assembly e uno di questi assembly è un wrapper MC++ della nostra libreria C++. L'applicazione è un'app per console. La maggior parte delle volte funziona bene, ma occasionalmente si blocca senza errori o eccezioni.Applicazione .NET si blocca con deadlock thread GC

Usando i dump e i simboli di memoria siamo riusciti a fare qualche diagnosi in WinDbg ma non sono sicuro che quello che stiamo vedendo sia un deadlock o meno. Ho cercato i nomi dei metodi CLR presenti nello stack ma non sono stato in grado di trovare casi in cui un thread sta tentando di allocare memoria e si blocca con GC.

Finora ho provato WinDbg con le estensioni sos, sosex, psscor4. Interessante sosex ha un comando per verificare i deadlock (! Dlk) ma non segnala deadlock.

È difficile pubblicare il codice perché è un'app ampia e complessa. Esiste una combinazione di assembly .NET 3.5 e 4.0. Esistono thread sia nel codice gestito che non gestito.

Sarei grato se qualcuno potesse esaminare le tracce dello stack e confermare che si tratta di un possibile deadlock con thread GC. O ancora meglio se puoi suggerire un altro modo per eseguire il debug di deadlock/hang in app .NET che usano C# e MC++.

Ecco quello che ho finora:

Elenco dei thread quando l'applicazione si blocca:

ThreadCount:  8 
UnstartedThread: 0 
BackgroundThread: 5 
PendingThread: 0 
DeadThread:  0 
Hosted Runtime: no 
              PreEmptive             Lock 
     ID OSID  ThreadOBJ  State GC  GC Alloc Context     Domain   Count APT Exception 
    0 1 de0 00000000008069f0  a020 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 MTA 
    2 2 2130 000000000080bd30  b220 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 MTA (Finalizer) 
    4 3 14fc 000000001d182880 200b020 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 MTA 
    5 4 20d0 000000001d18b400  b220 Enabled 0000000000000000:0000000000000000 00000000007fa280  2 MTA (GC) 
    6 5 18a8 000000001d19f6a0  b020 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 MTA 
    7 6 18a0 000000001d1c6f10  220 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 Ukn 
    8 7 12f4 000000001d1c1ee0  220 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 Ukn 
    10 8 2170 000000001d1c2ad0  220 Enabled 0000000000000000:0000000000000000 00000000007fa280  0 Ukn 

     OSID  Special thread type 
    1 2570 DbgHelper 
    2 2130 Finalizer 
    5 20d0 SuspendEE 
    12 1890 GC 

Questo è ciò che lo stack del thread GC assomiglia (discussioni!):

OS Thread Id: 0x1890 (12) 
Child-SP   RetAddr   Call Site 
0000000023e9f898 000000007799e4e8 ntdll!ZwWaitForSingleObject+0xa 
0000000023e9f8a0 000000007799e3db ntdll!RtlpWaitOnCriticalSection+0xe8 
0000000023e9f950 000007fef95d603e ntdll!RtlEnterCriticalSection+0xd1 
0000000023e9f980 000007fef947bc41 clr!UnsafeEEEnterCriticalSection+0x1f 
0000000023e9f9b0 000007fef947613a clr!CrstBase::Enter+0x1a1 
0000000023e9f9f0 000007fef95da3a2 clr!ThreadStore::LockThreadStore+0x9a 
0000000023e9fa20 000007fef9679675 clr!WKS::GCHeap::SuspendEE+0x82 
0000000023e9fb20 000007fef9677eb2 clr!WKS::gc_heap::bgc_suspend_EE+0x25 
0000000023e9fb50 000007fef98455b0 clr!WKS::gc_heap::background_mark_phase+0x236 
0000000023e9fbb0 000007fef9677b76 clr! ?? ::FNODOBFM::`string'+0x9f85d 
0000000023e9fc00 00000000773d652d clr!WKS::gc_heap::gc_thread_function+0xd3 
0000000023e9fc30 000000007797c521 KERNEL32!BaseThreadInitThunk+0xd 
0000000023e9fc60 0000000000000000 ntdll!RtlUserThreadStart+0x1d 

Per me sembra che il thread GC sia in attesa della sezione critica. Siamo stati in grado di trovare l'indirizzo della sezione critica e quindi trovare il thread proprietario per esso (! Critsec). Lo stack per il thread proprietario ha un aspetto simile al seguente. L'ho tagliato per farla breve per questo post. (! Dumpstack)

OS Thread Id: 0x20d0 (5) 
Child-SP   RetAddr   Call Site 
000000001fc5dd38 000007fefe0510dc ntdll!ZwWaitForSingleObject+0xa 
000000001fc5dd40 000007fef9478817 KERNELBASE!WaitForSingleObjectEx+0x79 
000000001fc5dde0 000007fef94787c0 clr!CLREvent::WaitEx+0x170 
000000001fc5de20 000007fef947866b clr!CLREvent::WaitEx+0xf8 
000000001fc5de80 000007fef967a15b clr!CLREvent::WaitEx+0x5e 
000000001fc5df20 000007fef967a001 clr!WKS::gc_heap::user_thread_wait+0x49 
000000001fc5df50 000007fef95dbb4e clr! ?? ::FNODOBFM::`string'+0x9fcc4 
000000001fc5e030 000007fef95da22e clr!WKS::GCHeap::GarbageCollectGeneration+0x14e 
000000001fc5e080 000007fef95d9e4e clr!WKS::gc_heap::try_allocate_more_space+0x25f 
000000001fc5e150 000007fef95d9fc8 clr!WKS::GCHeap::Alloc+0x7e 
000000001fc5e180 000007fef947407c clr!AllocateArrayEx+0xa6b 
000000001fc5e2f0 000007fef8555b75 clr!JIT_NewArr1+0x45c 
000000001fc5e4c0 000007fef8561103 mscorlib_ni!System.Reflection.CustomAttributeData.GetCustomAttributeRecords(System.Reflection.RuntimeModule, Int32)+0x115 
000000001fc5e590 000007fef855db55 mscorlib_ni!System.Reflection.CustomAttribute.IsCustomAttributeDefined(System.Reflection.RuntimeModule, Int32, System.RuntimeType, Boolean)+0x103 
000000001fc5e720 000007fef856c8ac mscorlib_ni!System.Reflection.CustomAttribute.IsDefined(System.RuntimeType, System.RuntimeType, Boolean)+0x75 
000000001fc5e770 000007fef857fe46 mscorlib_ni!System.Enum.InternalFormat(System.RuntimeType, System.Object)+0x2c 
000000001fc5e7b0 000007fef8554f3b mscorlib_ni!System.Text.StringBuilder.AppendFormat(System.IFormatProvider, System.String, System.Object[])+0x2e6 
000000001fc5e850 000007ff03c640fc mscorlib_ni!System.String.Format(System.IFormatProvider, System.String, System.Object[])+0x7b 
000000001fc5e8b0 000007ff03c638a6 MyLibrary1!NumberCache.NumberEntry.ToString()+0x26c 
+0

È possibile utilizzare Debug Diag per analizzare il dump, in quanto può analizzare diversi schemi di deadlock più di sosex. Tuttavia, nessuno strumento automatico è in grado di identificare tutti i modelli, poiché a volte lo stallo è troppo complicato per essere compreso da un essere umano. Se puoi permetterti, apri un caso di supporto tramite http://support.microsoft.com per consultare Microsoft. –

+0

@LexLi, grazie per il suggerimento Debug Diag. L'avevo già incontrato ma l'ho respinto perché sembrava essere troppo specifico per IIS. L'ho eseguito su uno dei precedenti mem dump ed è fondamentalmente sottolineando la stessa cosa: il thread GC in attesa di crit sec di proprietà di un altro thread. Che tipo di conferma mi dà lo stallo. Sembra che Debug Diag usi la stessa estensione psscor4. Inoltre stiamo esaminando anche l'opzione di supporto MS. Grazie! – user1210698

+0

Penso che dovresti controllare tutte le implementazioni del finalizzatore (~). Sembra che tu stia bloccando definitivamente i tuoi finalizzatori – 6opuc

risposta

2

Questa linea nel secondo stack sembra sospetto:

000000001fc5df50 000007fef95dbb4e clr! ?? ::FNODOBFM::`string'+0x9fcc4 

Guardate quanto è grande l'indirizzo offset è, e non vedo alcun nome del modulo - che le manca un po ' simboli?

Forse c'è un finalizzatore in quella libreria che sta causando un problema.

+0

Non sono sicuro dei simboli, anche se il metodo sembra essere parte di CLR. – user1210698

+0

Non sono sicuro dei simboli, sebbene il metodo appaia come parte di CLR. Inoltre, non utilizza alcun COM, solo C# e gli assembly C++ gestiti. In ogni caso, abbiamo apportato alcune modifiche, come modificato alcune istruzioni di blocco in Monitor.Enter con timeout, aggiunto un GC.Collect all'interno di una logica che stava allocando molti piccoli oggetti. Alla fine non abbiamo visto il problema negli ultimi mesi. – user1210698

+0

spesso, un grande offset: + 0x9fcc4 può significare che il frame dello stack nella lista era solo l'ultimo posto che il debugger poteva trovare, e quindi l'offset grande appare come la differenza tra l'ultimo punto conosciuto e il punto di esecuzione corrente davvero era. Tuttavia, ho cercato su google "FNODOBFM" e ho trovato questo: http://bit.ly/KTLcZh e questo: http://bit.ly/J5HqPJ (bitly per non eccedere i caratteri max per questo commento). Penso che quella linea potrebbe essere dovuta al riarrangiamento dell'ottimizzatore - rende difficile conoscere la pila reale. Forse quegli articoli possono aiutare. – JMarsch

0

Non sono un grande esperto, ma ero solo curioso e mi chiedevo se il thread del finalizzatore si è bloccato (intendo l'eccezione non gestita nel thread del finalizzatore) mentre blocchi alcune risorse e il thread GC sta cercando di acquisirlo?