Rastreamento de referência de objeto com marcas

Objetos kernel são objetos de dados primitivos que o kernel do Windows implementa na memória do sistema. Eles representam entidades como dispositivos, drivers, arquivos, chaves do Registro, eventos, semáforos, processos e threads.

A maioria dos objetos kernel não é permanente. Para impedir que o Windows exclua um objeto kernel não persistente enquanto um driver de modo kernel o está usando, o driver obtém uma referência contada ao objeto . Quando o driver não precisa mais do objeto , o driver libera sua referência ao objeto .

Se um driver não liberar todas as suas referências a um objeto, a contagem de referência do objeto nunca atingirá zero e o Gerenciador de Objetos nunca o excluirá. Não é possível reutilizar recursos vazados até que o sistema operacional seja reiniciado.

Outro tipo de erro de referência ocorrerá se um driver em fizer referência a um objeto . Nesse caso, o driver libera mais referências a um objeto do que o driver realmente contém. Esse erro pode fazer com que o Gerenciador de Objetos exclua o objeto prematuramente, enquanto outros clientes ainda estão tentando acessar o objeto.

Vazamentos e sub-referências de objetos kernel podem ser bugs difíceis de rastrear. Por exemplo, um objeto de processo ou um objeto de dispositivo pode ter dezenas de milhares de referências. Pode ser difícil identificar a origem de um bug de referência de objeto nessas circunstâncias.

No Windows 7 e versões posteriores do Windows, você pode fornecer uma marca para referências de objeto para facilitar a localização desses bugs. As seguintes rotinas associam marcas à aquisição e liberação de referências a objetos kernel:

ObDereferenceObjectDeferDeleteWithTag

ObDereferenceObjectWithTag

ObReferenceObjectByHandleWithTag

ObReferenceObjectByPointerWithTag

ObReferenceObjectWithTag

Por exemplo, ObReferenceObjectWithTag e ObDereferenceObjectWithTag, que estão disponíveis no Windows 7 e versões posteriores do Windows, são versões aprimoradas das rotinas ObReferenceObject e ObDereferenceObject , que estão disponíveis no Windows 2000 e versões posteriores do Windows. Essas rotinas aprimoradas permitem que você forneça um valor de marca personalizada de quatro bytes como um parâmetro de entrada. Você pode usar as ferramentas de depuração do Windows para inspecionar um rastreamento de referência de objeto que contém o valor da marca para cada chamada . ObReferenceObject e ObDereferenceObject não permitem que o chamador especifique marcas personalizadas, mas, no Windows 7 e versões posteriores do Windows, essas rotinas adicionam marcas padrão (com o valor de marca "Dflt") ao rastreamento. Portanto, uma chamada para ObReferenceObject ou ObDereferenceObject tem o mesmo efeito que uma chamada para ObReferenceObjectWithTag ou ObDereferenceObjectWithTag que especifica um valor de marca de "Dflt". (No programa, esse valor de marca aparece como 0x746c6644 ou 'tlfD'.)

Para rastrear um possível vazamento de objeto ou sub-referência, identifique um conjunto de chamadas ObReferenceObjectXxxWithTag e ObDereferenceObjectXxxWithTag associadas no driver que incrementam e decrementam a contagem de referência de um objeto específico. Escolha um valor de marca comum (por exemplo, "Lky8") a ser usado para todas as chamadas nesse conjunto. Depois que o driver terminar de usar um objeto , o número de decrementos deverá corresponder exatamente ao número de incrementos. Se esses números não corresponderem, o driver terá um bug de referência de objeto. O depurador pode comparar o número de incrementos e decrementos de cada valor de marca e informar se eles não correspondem. Com essa funcionalidade, você pode identificar rapidamente a origem da incompatibilidade de contagem de referências.

Para exibir um rastreamento de referência de objeto nas ferramentas de depuração do Windows, use a extensão do depurador !obtrace kernel-mode. Se o rastreamento de referência de objeto estiver ativado, você poderá usar a extensão !obtrace para exibir marcas de referência de objeto. Por padrão, o rastreamento de referência de objeto está desativado. Use o Editor de Sinalizadores Globais (Gflags) para habilitar o rastreamento de referência de objeto. Para obter mais informações sobre o Gflags, consulte Configurando o rastreamento de referência de objeto.

A saída da extensão !obtrace inclui uma coluna "Tag", como mostra o exemplo a seguir:

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

A última linha neste exemplo indica que as contagens de referência e desreferência associadas à marca "Lky8" não correspondem e que o resultado dessa incompatibilidade é uma referência excessiva por um (ou seja, um vazamento).

Se o resultado fosse, em vez disso, uma sub-referência, a última linha da saída !obtrace poderia ser a seguinte:

Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1

Por padrão, o sistema operacional conserva a memória excluindo o rastreamento de referência de objeto para um objeto depois que ele libera o objeto. Manter um rastreamento na memória mesmo depois que o sistema libera um objeto pode ser útil ao rastrear uma sub-referência. Para essa finalidade, a ferramenta Gflags fornece uma opção "Permanente", que preserva o rastreamento na memória enquanto o computador é desligado e é iniciado novamente.

O Windows XP introduziu o rastreamento de referência de objeto. Como inicialmente o rastreamento não incluía marcas, os desenvolvedores tinham que usar técnicas menos convenientes para identificar bugs de referência de objeto. O depurador pode acompanhar as referências de grupos de objetos, que o desenvolvedor selecionou por tipo de objeto. A única maneira de o desenvolvedor identificar as várias fontes de referências de objeto e desreferências era comparar suas pilhas de chamadas. Embora o exemplo !obtrace anterior contenha apenas cinco pilhas, determinados tipos de objeto, como um objeto de processo (EPROCESS), podem ser referenciados e desreferenciados muitas milhares de vezes. Com milhares de pilhas a serem inspecionadas, pode ser difícil identificar a origem de um vazamento de objeto ou sub-referência sem usar marcas.