Traccia dei riferimenti a oggetti con tag
Gli oggetti kernel sono oggetti dati primitivi che il kernel di Windows implementa nella memoria di sistema. Rappresentano entità come dispositivi, driver, file, chiavi del Registro di sistema, eventi, semafori, processi e thread.
La maggior parte degli oggetti kernel non è permanente. Per impedire a Windows di eliminare un oggetto kernel non gestito mentre un driver in modalità kernel lo usa, il driver ottiene un riferimento conteggiato all'oggetto. Quando il driver non ha più bisogno dell'oggetto, il driver rilascia il riferimento all'oggetto.
Se un driver non rilascia tutti i riferimenti a un oggetto, il conteggio dei riferimenti dell'oggetto non raggiunge mai zero e Gestione oggetti non lo elimina mai. Non è possibile riutilizzare le risorse perse fino al riavvio del sistema operativo.
Un altro tipo di errore di riferimento si verifica se un driver fa riferimento a un oggetto. In questo caso, il driver rilascia più riferimenti a un oggetto rispetto al driver effettivamente contenuto. Questo errore può causare l'eliminazione prematura dell'oggetto da parte di Gestione oggetti, mentre altri client stanno ancora tentando di accedere all'oggetto.
Le perdite e i riferimenti di oggetti kernel possono essere difficili da rilevare. Ad esempio, un oggetto process o un oggetto dispositivo potrebbe avere decine di migliaia di riferimenti. Può essere difficile identificare l'origine di un bug di riferimento a oggetti in queste circostanze.
In Windows 7 e versioni successive di Windows è possibile specificare un tag per i riferimenti agli oggetti per semplificare la ricerca di questi bug. Le routine seguenti associano i tag all'acquisizione e al rilascio di riferimenti agli oggetti kernel:
ObDereferenceObjectDeferDeleteWithTag
ObReferenceObjectByHandleWithTag
ObReferenceObjectByPointerWithTag
Ad esempio, ObReferenceObjectWithTag e ObDereferenceObjectWithTag, disponibili in Windows 7 e versioni successive di Windows, sono versioni avanzate delle routine ObReferenceObject e ObDereferenceObject , disponibili in Windows 2000 e versioni successive di Windows. Queste routine avanzate consentono di specificare un valore di tag personalizzato a quattro byte come parametro di input. È possibile usare gli strumenti di debug di Windows per esaminare una traccia di riferimento a oggetti contenente il valore del tag per ogni chiamata. ObReferenceObject e ObDereferenceObject non consentono al chiamante di specificare tag personalizzati, ma, in Windows 7 e versioni successive di Windows, questi routine aggiungono tag predefiniti (con il valore tag "Dflt") alla traccia. Pertanto, una chiamata a ObReferenceObject o ObDereferenceObject ha lo stesso effetto di una chiamata a ObReferenceObjectWithTag o ObDereferenceObjectWithTag che specifica un valore tag di "Dflt". Nel programma questo valore tag viene visualizzato come 0x746c6644 o 'tlfD'.
Per tenere traccia di una potenziale perdita di oggetti o di riferimento, identificare un set di chiamate ObReferenceObjectXxxWithTag e ObDereferenceObjectXxxnel driver che incrementa e decretano il conteggio dei riferimenti di un determinato oggetto. Scegliere un valore di tag comune ,ad esempio "Lky8") da usare per tutte le chiamate in questo set. Al termine dell'uso di un oggetto, il numero di decrementi deve corrispondere esattamente al numero di incrementi. Se questi numeri non corrispondono, il driver ha un bug di riferimento dell'oggetto. Il debugger può confrontare il numero di incrementi e decrementi per ogni valore di tag e indicare se non corrispondono. Con questa funzionalità è possibile individuare rapidamente l'origine della mancata corrispondenza del conteggio dei riferimenti.
Per visualizzare una traccia di riferimento a oggetti negli strumenti di debug di Windows, usare l'estensione del debugger in modalità kernel !obtrace . Se la traccia dei riferimenti all'oggetto è attiva, è possibile usare l'estensione !obtrace per visualizzare i tag di riferimento degli oggetti. Per impostazione predefinita, la traccia dei riferimenti a oggetti è disattivata. Usare l'editor di flag globali (Gflags) per abilitare la traccia dei riferimenti a oggetti. Per altre informazioni su Gflags, vedere Configurazione della traccia dei riferimenti a oggetti.
L'output dell'estensione !obtrace include una colonna "Tag", come illustrato nell'esempio seguente:
0: kd> !obtrace 0x8a226130
Object: 8a226130
Image: leakyapp.exe
Sequence (+/-) Tag Stack
-------- ----- ---- --------------------------------------------
36 +1 Dflt nt!ObCreateObject+1c4
nt!NtCreateEvent+93
nt!KiFastCallEntry+12a
37 +1 Dflt nt!ObpCreateHandle+1c1
nt!ObInsertObjectEx+d8
nt!ObInsertObject+1e
nt!NtCreateEvent+ba
nt!KiFastCallEntry+12a
38 -1 Dflt nt!ObfDereferenceObjectWithTag+22
nt!ObInsertObject+1e
nt!NtCreateEvent+ba
nt!KiFastCallEntry+12a
39 +1 Lky8 nt!ObReferenceObjectByHandleWithTag+254
leakydrv!LeakyCtlDeviceControl+6c
nt!IofCallDriver+63
nt!IopSynchronousServiceTail+1f8
nt!IopXxxControlFile+6aa
nt!NtDeviceIoControlFile+2a
nt!KiFastCallEntry+12a
3a -1 Dflt nt!ObfDereferenceObjectWithTag+22
nt!ObpCloseHandle+7f
nt!NtClose+4e
nt!KiFastCallEntry+12a
-------- ----- ---- --------------------------------------------
References: 3, Dereferences 2
Tag: Lky8 References: 1 Dereferences: 0 Over reference by: 1
L'ultima riga in questo esempio indica che i conteggi di riferimento e dereferenza associati al tag "Lky8" non corrispondono e che il risultato di questa mancata corrispondenza è un riferimento eccessivo per uno (ovvero una perdita).
Se il risultato era invece un riferimento inferiore, l'ultima riga dell'output !obtrace potrebbe essere la seguente:
Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1
Per impostazione predefinita, il sistema operativo conserva la memoria eliminando la traccia di riferimento dell'oggetto per un oggetto dopo aver liberato l'oggetto. Mantenere una traccia in memoria anche dopo che il sistema libera un oggetto può essere utile quando si tiene traccia di un riferimento inferiore. A questo scopo, lo strumento Gflags offre un'opzione "Permanente", che mantiene la traccia in memoria mentre il computer viene arrestato e riavviato.
Windows XP ha introdotto la traccia di riferimento all'oggetto. Poiché inizialmente la traccia non includeva tag, gli sviluppatori dovevano usare tecniche meno utili per identificare i bug di riferimento agli oggetti. Il debugger può tenere traccia dei riferimenti di gruppi di oggetti, che lo sviluppatore selezionato per tipo di oggetto. L'unico modo in cui lo sviluppatore poteva identificare le varie origini dei riferimenti agli oggetti e le dereferenze era quello di confrontare gli stack di chiamate. Anche se l'esempio !obtrace precedente contiene solo cinque stack, alcuni tipi di oggetto, ad esempio un oggetto EPROCESS ,potrebbero essere a cui fa riferimento e dereferenziare molte migliaia di volte. Con migliaia di stack da controllare, potrebbe essere difficile identificare l'origine di una perdita di oggetti o un riferimento inferiore senza usare tag.