Come disabilitare il Fault Tolerant Heap
Una delle novità introdotte da Windows 7 è il Fault Tolerant Heap. Silviu Calinoiu, il papà dell’idea, ha descritto efficacemente il FTH in questo video.
Silviu mi ha aiutato personalmente a risolvere un problema con un cliente e vorrei riportarvi questa esperienza, perchè ho potuto apprezzare alcuni suggerimenti che Silviu mi ha dato per arrivare a capire come mai non si riuscisse a disabilitare l’FTH con l’impostazione da sistema.
Scenario: applicazione che si sta sviluappando da anni, in VS 2005, e arriva il momento di migrare a Windows 7. L’applicazione è scritta in C++, e si sta lavorando con la versione debug. Il Debug è lentisssssssiiiiimo. Lo sviluppatore riporta uno stack della sua applicazione per dimostrare che la colpa è dell’FTH.
AcXtrnal.dll!NS_FaultTolerantHeap::FthDelayFreeQueueFlush() + 0x31 bytes
AcXtrnal.dll!NS_FaultTolerantHeap::FthValidateHeap() + 0x60 bytes
KernelBase.dll!_HeapValidate@12() + 0x14 bytes
msvcr80d.dll!_CrtIsValidHeapPointer(const void * pUserData=0x091cea68) Line 2072
msvcr80d.dll!_free_dbg_nolock(void * pUserData=0x091cea68, int nBlockUse=0x00000001) Line 1279 + 0x9 bytes
msvcr80d.dll!_free_dbg(void * pUserData=0x091cea68, int nBlockUse=0x00000001) Line 1220 + 0xd bytes
msvcr80d.dll!free(void * pUserData=0x091cea68) Line 1178 + 0xb bytes
Effettivamente AcXtrnal.dll è una delle librerie dove risiedono gli “shim” dell’FTH. Le librerie sono esattamente acgenral.dll, aclayers.dll e acxtrnal.dll. Non sono caricate tutte insieme contemporaneamente. Dipende da quali “shim” sono applicati al momento.
L’interesse principale del cliente però in questo caso, era di disabilitare l’FTH, perchè per il momento è in debug, e quindi si attende già dei problemi, anzi li vuole vedere per poterli risolvere. Quindi ha fatto un po’ di ricerche su Internet fino a trovare questo articolo: http://msdn.microsoft.com/en-us/library/dd744764(VS.85).aspx
L’articolo testualmente dice che per disabilitare l’FTH a livello di sistema bastano due operazioni:
- impostare a 0 le seguenti chiavi di registry:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\FTH\ Enabled
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\FTH\ Enabled
- stoppare e riavviare il servizio “Diagnostic Policy Service” (DPS).
Provando però a seguire quelle istruzioni, non cambia nulla. Quindi vuol dire che per qualche motivo l’FTH è ancora attivo per il processo in oggetto.
Qui iniziano gli step di troubleshooting suggeriti da Silviu.
1 AppCompatFlags Layers
Cercare nel registry le seguenti chiavi
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Wow6432Node\Windows NT\CurrentVersion\AppCompatFlags\Layers
e cercare in quelle il nome del proprio processo, qualcosa del tipo
C:\Program files\ …\YOURPROCESS.exe REG_SZ FaultTolerantHeap e se lo si trova cancellare quel valore.
2 verificare che fthsvc.dll non sia caricata in memoria
fthsvc.dll è la parte server dell’FTH. Le tre dll che abbiamo visto prima, vengono caricate “in process” e comunicano poi con la parte server contenuta nel servizio DPS. Se la dll è caricata, significa che il servizio sta operando, e che quindi qualche applicazione nel sistema sta girando con l’FTH applicato ad essa.
Per fare ciò basta eseguire questo comando da un Command Prompt “elevato” (‘As Administrator’). Se non ci sono processi con l’FTH applicato, questo deve essere l’output:
3 interrompere il servizio DPS
Come abbiamo appena visto, il servizio DPS è la parte “server” della gestione dell’FTH. Interrompendo questo servizio, automaticamente l’FTH non funziona più, anche se le dll client degli “shim” applicati all’applicazione sono ancora caricate in memoria nel processo e funzionano, cercando di collegarsi al servizio. Quindi, non funzionano in pieno, ma possono comunque rallentare ancora un pochino le operazioni. Questo però è già un buon workaround, se non si ha tempo di investigare a fondo la problematica.
4 Winbdg
Windbg, è lo strumento principe degli sviluppatori. E’ un debugger multipurpose, Kernel e User Mode, e grazie a quello si possono fare ulteriori verifiche.
Winbdg esiste a 32 e 64 bit. I processi a 32 bit vanno debuggati con la versione a 32 bit anche se girano su un sistema a 64 bit. Quindi occorre scaricare la versione corretta relativa all’applicazione che stiamo debuggando.
Winbdg è disponibile qui:
http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx
http://www.microsoft.com/whdc/devtools/debugging/installx64.mspx
Per il corretto funzionamento di Windbg, è necessario configurare il Symbol Server. I simboli pubblici di tutti i prodotti Microsoft sono disponibili online. Basta impostare in Windbg la seguente Symbol path:
SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
I simboli pubblici di acxtrnal.dll, acxtrnal.pdb, contengono alcune inrformazioni aggiuntive, per facilitare il debug di casi come questo. Per l’utilizzo base di Windbg vi rimando a post fatti da miei colleghi (Tess, Alejandro, Carlo, Doug) che ne sanno molto più di me sull’argomento. Entriamo subito nel dettaglio, dando per scontato che sappiamo come attaccare il debugger al processo da controllare (Menu File->Atatch to Process) e che siamo al breakpoint.
L’FTH può essere attivato autonomamente perchè il servizio FTH ha detectato un crash nel sistema, o esplicitamente se il processo viene eseguito in qualche Application Compatibility Mode. Possiamo capire esattamente il motivo verificando la variabile interna FthClient.
Se siamo al breakpoint, eseguiamo il seguente comando:
0:050> x acxtrnal!*fth*client*
Che ci ritorna l’indirizzo in memoria della variabile:
…
74750cc8 AcXtrnal!NS_FaultTolerantHeap::FthClient = <no type information>
…
Che a questo punto andremo a leggere:
0:050> dt acxtrnal!__FTH_CLIENT 74750cc8
+0x000 TrackingState : 2
+0x004 QueryingState : 0
+0x008 ServerEnabled : 1
+0x00c ConnectionAttemptsAllowed : 15
+0x010 ConnectionSkipsCounter : 256
+0x014 ConnectionsAttempted : 1
+0x018 Watson : 0x000a0000 __FTH_WATSON
+0x01c WatsonInPassiveMode : __FTH_WATSON
+0x0a4 WatsonSection : 0x00000068
+0x0a8 WatsonView : 0x000a0000
+0x0ac ClientLock : _RTL_CRITICAL_SECTION
+0x0c4 PipeHandle : 0xffffffff
+0x0c8 PipeEvent : 0x00000038
+0x0cc PipeOverlapped : _OVERLAPPED
+0x0e0 MaximumAllocOverhead : 0x1000000
+0x0e4 MaximumFreeOverhead : 0x400000
+0x0e8 MaximumWatsonOverhead : 0x10000
I campi più interessanti in questa struttura sono:
TrackingState - Il valore 2 significa che FTH è attivo. 0 significa che FTH non è attivo ma sta cercando di ottenere il permesso dal servizio per attivarsi. 1 significa che FTH non è attivo perchè il servizio FTH ha negato il permesso alla richiesta di attivazione.
ServerEnabled - 1 significa che FTH è stato attivato dal sevizio DPS. 0 significa che è stato attivato attraverso qualche Application Compatibility Mode.
Watson - Un grande contenitore di dati che tiene traccia dei probelmi che l’FTH ha mitigato.
Supponiamo questo sia l’output nel nostro caso reale:
0:050> dt acxtrnal!__FTH_CLIENT 74750cc8
+0x000 TrackingState : 2 <- FTH is enabled
+0x004 QueryingState : 0
+0x008 ServerEnabled : 0 <- compatibility mode
Questo può accadere per moltissimi “shim” applicati al programma. Si può a questo punto investigare quali shim sono stati applicati esattamente con questa serie di comandi:
0:001> x acgenral!*shimused*
5de7aa8c AcGenral!NS_LoadLibraryCWD::g_bSubshimUsed = 1
5de7a95c AcGenral!NS_EmulateOldPathIsUNC::g_bSubshimUsed = 0
5de7a944 AcGenral!NS_DisableW2KOwnerDrawButtonStates::g_bSubshimUsed = 0
… lots of output …
Simili comandi possono essere usati anche con le altre due librerie che abbiamo citato:
0:001> x aclayers!*shimused*
… lots of output …
0:001> x acxtrnal!*shimused*
… lots of output …
Come vedete i nomi degli “shim” applicati sono nel nome stesso della variabile, per cui a quel punto è facile capire quali sono stati applicati.
5 Process Monitor
Process Monitor, è uno strumento potentissimo, se usato con le giuste informazioni. Va quindi non solo installato, ma anche configurato correttamente. Il cuore di tutto, è la dbghelp.dll che permette a seconda della versione utilizzata di mappare o meno le informazioni dello stack con i simboli di debug e quindi con la vera e propria storia dell’applicazione. Ogni sistema operativo fornisce una sua dbghelp.dll, ma che non è in grado di fornire tutte le informazioni necessarie a Process Monitor. Occorre quella installata con Windbg.
Questi sono i passi per configurare correttamente Process Monitor.
1. Installate l’ultimo windbg nella vesione del sistema operativo e dell’applicazione che dovete monitorare da
http://www.microsoft.com/whdc/devtools/debugging/installx64.mspx in C:\Debuggers64
o da
http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx in C:\debuggers
2. Installate Process Monitor da http://www.microsoft.com/technet/sysinternals/utilities/processmonitor.mspx
Process Monitor contiene entrambe le versioni 32 e 64 bit.
3. Configurate Process monitor per usare la dbghelp.dll che viene con windbg, nella versione 32 o 64 bit. Aprite Process Monitor, Options-> Configure Symbols
e qui selezionate C:\Debuggers\dbghelp.dll nel caso stiate usando ProcMon a 32 bit,
C:\Debuggers64\dbghelp.dll nel caso stiate usando la versione a 64 bit.
Per avviare ProcMon a 32 bit, nel caso siate su un sistema a 64 bit e dobbiate monitorare una applicazione a 32 bit, dovete usare lo switch da linea di comando “/Run32”.
Nel caso di “shim” applicate via Application Compatibility Mode, troveremo nel log di Process Monitor delle entry tipo queste:
12:13:03.4605594 PM explorer.exe 4996 3512 RegQueryValue HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\devenv.exe SUCCESS Type: REG_SZ, Length: 40, Data: VISTARTM RUNASADMIN
12:13:03.3449159 PM explorer.exe 4996 3512 RegQueryValue HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\devenv.exe SUCCESS Type: REG_SZ, Length: 40, Data: VISTARTM RUNASADMIN
12:13:03.6322082 PM explorer.exe 4996 3512 RegQueryValue HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\C:\Program Files\La nostra app (64-bit)\System\AppStart.exe SUCCESS Type: REG_SZ, Length: 58, Data: DISABLEUSERCALLBACKEXCEPTION
Come potete notare, per ogni eseguibile a cui è applicata una “shim”, troviamo sotto la chiave
HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers o HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
il nome dell’eseguibile a cui la “shim” è applicata e i nomi delle “shim” applicate.
Cosa comprende quella specifica “shim” è possibile capirlo installando l’Application Compatibility Toolkit 5.5.
Questo tool non dovrebbe mancare dalla dotazione standard di uno sviluppatore o di un system administrator che ha la responsailità della migrazione ad un nuovo sistema operativo, per verificare la compatibilità delle applicazioni aziendali col nuovo sistema.
Come potete vedere, nel caso in oggetto, a Devenv.exe, erano applicate due “shim”: VistaRTM e RunAsAdmin.
ACT 5.5 mostra che la “shim” VistaRTM contiene tra le varie mitigazioni anche il FaultTolerantHeap, che quindi verrà abilitato per tutti quegli applicativi che vengono esguiti con questa “shim”.
Conclusioni.
Abbiamo visto quindi come fare il troubleshooting, usando diversi strumenti, per arrivare a capire via step successivi come e perchè il Fault Tolerant Heap sia stato applicato (per via di una “shim” di Application Compatibility) e a chi (devenv.exe) nonostante lo si sia disabilitato esplicitamente a livello di sistema. E quindi come disabilitare il Fault Tolerant Heap, se necessario, per singola applicazione.
Questo è potuto accadere perchè l’FTH può essere attivato in entrambi i modi: da configurazione di sistema, e da Application Compatibility.
Stoppare il servizio DPS, può essere un workaround per mitigare temporaneamente la situazione, mentre si eseguono ulteriori step di troubleshooting per arrivare a determinare la vera causa del problema.
Alla prossima!
Mario Raccagni
Senior Support Engineer
Platform Development Support Team