Quantos handles sua aplicação está abrindo?

2016/11/29

Mesmo que você não programe em C/C++, mas programe para Windows (ex: .NET), sempre há a possibilidade de seu programa estar causando leaks de handles indefinidamente, o que não se traduz em aumento significativo de memória alocada para seu processo, mas é, sim, um problema a ser tratado.

Como isso pode ser causado?

Bom, em C/C++ sempre é mais simples de entender esses conceitos. Um código simples que se esquece de fechar o handle usando CloseHandle ou a função equivalente do recurso obtido já seria o suficiente. O último bug que eu encontrei em um código desses comete o clássico erro de sair no meio da função, deixando os recursos alocados:

DWORD ClassicHandleLeak()
{
	DWORD ret = 0;
	HKEY hKey;

	if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Something", 
		0, GENERIC_READ, &hKey) == ERROR_SUCCESS )
	{
		DWORD retSz = sizeof(ret);

		if (RegQueryValueEx(hKey, L"SomeValue", NULL, NULL, 
			(PBYTE) &ret, &retSz) == ERROR_SUCCESS)
		{
			// success!
			return ret;
		}

		RegCloseKey(hKey);
	}

	return ret;
}

No exemplo acima quando as coisas dão certo elas também dão errado, já que o retorno do valor no meio da função evita que o HANDLE armazenado em hKey seja desalocado.

E como fazer para descobrir esse tipo de leak?

HandleLeaker

O HandleLeaker é apenas um exemplo de aplicação que realiza o leak de um handle por segundo. Ele tenta (e consegue) abrir um handle para seu próprio processo, e deixa o handle aberto (programas em Win32 API não são muito bons em RAII).

int main()
{
	while (true)
	{
		HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
		Sleep(1000);
	}
}

Performance Monitor

O Perfmon(.msc) está aí no Windows já faz algumas versões (quase todas). Tudo que você precisa para executá-lo é executar o comando perfmon no diálogo de execução (Start, Run) ou encontrar o atalho para perfmon.msc. Na busca do Windows 810 também é possível encontrá-lo pelo nome.

Ao executá-lo a primeira coisa que ele monitora é o processamento da máquina. Podemos eliminar ou esconder esse indicador direto na lista abaixo da ferramenta.

Existem incontáveis contadores no Perfmon. Para o que precisamos vamos em Process e escolhemos o contador de Handles:

Depois de um tempo o Perfmon irá exibir o histórico que determina para onde está indo o seu contador:

Se os valores do seu contador estão fora da faixa do histórico é possível ajustar a escala nas propriedades:

Se a frequência for muito menor do que um handle por segundo (isso acontece, principalmente com serviços que rodam por dias/semanas/meses), é possível mudar também pelas propriedades, mas gerais:

A mudança que fizemos captura o dado monitorado de dez em dez segundos e realiza essa operação por 600 segundos (10 minutos), até repetir o gráfico de histórico:

Process Explorer

Outra forma de verificar como andam os handles da máquina é usando a já famosa ferramenta da SysInternals. Através das inúmeras colunas que ela fornece existe o contador de handles de cada processo, através do qual é possível verificar quais são os processos com mais handles abertos:

Se seu programa for um handle hog, vai conseguir até ver esse leak acontecendo em tempo real (como o nosso programa mal-educado):

E como encontrar o código-fonte responsável por esse leak? Mais detalhes em um próximo post.

Facebook | Twitter | Linkedin | Google