Desafio da Semana #1


DESAFIO DA SEMANA


 


Esses dias fiquei pensando sobre o que poderia colocar no blog, além de artigos técnicos, que pudesse ser interessante. Pensei em técnicas de depuração com WinDbg para código nativo ou gerenciado, mostrar uso de algumas ferramentas de troubleshooting, falar sobre incidentes interessantes que pegamos aqui nos times de escalações, etc… mas uma rápida pesquisa na internet me mostrou que existem blogs aos montes falando sobre esses assuntos. A grande maioria é em inglês, entretanto,  para os que não dominam o idioma, com conhecimento de inglês técnico apenas é possível se entender muitas coisas.


Pois bem, me ocorreu de disseminar conhecimento sobre a forma de desafios! Pesquisei sobre isso e não vi blogs que fizessem isso, portanto, creio ser uma forma interessante de se disseminar e compartilhar conhecimento.


A idéia é colocar uma situação fictícia de um cliente que pede seu auxílio. A situação é uma breve descrição dos sintomas apresentados por uma aplicação, o código fonte ou instruções sobre como reproduzir os sintomas e a descrição do seu objetivo.


Portanto como um fictício engenheiro de escalação, seu objetivo será de isolar o problema, propor a devida solução para corrigi-lo e explicar a estratégia utilizada para isolar o problema.


Após uma semana colocarei a resposta e comentarei os posts. As aplicações serão em C, C++, Visual Basic 6, Visual Basic .NET ou C#. O nível de dificuldade será justo pois o objetivo é que para que muitos consigam resolver os problemas. Na verdade, o interessante será observar a estratégia usada para se isolar o problema e a solução proposta, pois diferentes abordagens e soluções poderão ser aplicadas na maioria dos problemas!


 


Essa abordagem será um modo interessante de compartilharmos conhecimento pois acredito que muitos vão propor estratégias criativas para se isolar o problema ou soluções criativas para corrigi-lo!


 


Portanto, vamos ao primeiro da série!


 


 


DESAFIO DA SEMANA #1


 


Um cliente contactou você porque uma aplicação C aparenta estar consumindo memória sem nunca liberá-la. Seu objetivo é de certificar que o sintoma é esse mesmo, e, em seguida, identificar o problema causando esse sintoma, propor uma solução (solução prática que exija o mínimo de alterações na aplicação) e descrever as possíveis estratégias para se isolar esse problema.


 


Nota: Imagine que você tenha que descrever um modo de se identificar o problema de memória que seja válido para essa simples aplicação e para uma grande aplicação com milhares de linhas de código. Esse é o objetivo mais importante desse desafio.


 


INSTRUÇÕES


 


Crie uma aplicação console em Visual C++ com nome de MemoryLeak.exe e coloque o seguinte código:


 


 


#include “stdafx.h”


#include <windows.h>


#include <stdlib.h>


#include <string.h>


#include <stdio.h>


#include <conio.h>


#include <process.h>


 


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


// SINTOMAS:  Vazamento de memoria. (memory leak)


//


// OBJETIVO: Isolar o problema causando o memory leak e corrigir a aplicacao.


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


int _tmain(int argc, _TCHAR* argv[])


{


    _tprintf(_T(“Memory Leak Demo\n”));


 


            _tprintf(_T(“Pressione alguma tecla para iniciar…\n\n”));


            _getch();


 


    for(int i = 0; i < 50; i++)


    {


                        BYTE* pbyAllocated = (BYTE*) malloc (sizeof(BYTE) * (10000 * i));


 


                        if(pbyAllocated)


                        {


                                    _tprintf(_T(“Memoria alocada com sucesso!\n”));


                        }


                        else


                        {


                                    _tprintf(_T(“Alocacao de memoria falhou…\n”));


                        }


 


                        // Libera memoria


                        pbyAllocated = NULL;


 


                        Sleep(1000);


    }


 


            _tprintf(_T(“Pressione alguma tecla para finalizar…\n”));


            _getch();


            return 0;


}


 


 


Gere um executável versão Debug ou Release.


 


Coloque sua resposta aqui no blog no seguinte formato:


 


SINTOMA


 


(é um vazamento de memória ou de handle?)


 


PROBLEMA


 


(o que está ocasionando o leak?)


 


SOLUÇÃO


 


(qual a solução proposta para corrigir o problema?)


 


ESTRATÉGIA


 


(qual estratégia você usou para comprovar os sintomas e isolar o problema?)


 


 


Na semana que vem colocarei uma resposta para esse desafio, mas sempre que possível estarei lendo os comentários e respondendo eventuais dúvidas.


 


Até a próxima!


Roberto Farah

Comments (5)

  1. Danilo says:

    SINTOMA

    vazamento de memória…

    PROBLEMA

    a ausência de código para liberar a memória alocada dinamicamente

    SOLUÇÃO

    Desalocar a memória corretamente utilizando-se a função Free, para isso deve-se substituir o trecho:

    pbyAllocated = NULL;

    Por:

    Free(pbyAllocated);

    ESTRATÉGIA

    Para comprovação dos sintomas fiz a execução do aplicativo e monitorei a memória alocada utilizando-se um aplicativo de monitoração de processos simples (Process Explorer da SysInternals), com isso foi possível verificar que a cada passagem do FOR a memória ela alocada e nunca liberada. Para isolar o problema procurei no código por alocações de memória, foi encontrado o trecho:

    BYTE* pbyAllocated = (BYTE*) malloc (sizeof(BYTE) * (10000 * i));

    Visto isso procurei pelo código que faria a desalocaçao de pbyAlocated, quando encontrei o trecho:

    // Libera memoria

    pbyAllocated = NULL;

    Visto que não há nenhum outro trecho no código que tenta realizar a desalocação de memória, conclui que o código inserido no programa para realizar essa ação fora inserido equivocadamente, pois apenas setar o ponteiro para NULL não proporcionará a desalocação da memória.

  2. Marcelo CC Cortes says:

    SINTOMA

    A memória utilizada pelo programa está sempre aumentando enquanto o programa está sendo executado e não há evidencias de que a memória está sendo liberada. Provável memory leak.

    PROBLEMA

    Em C a maior causa de memory leaks é memória sendo alocada com malloc e nunca sendo liberada com free.

    SOLUÇÃO / ESTRATÉGIA

    Existem várias soluções, desde procurar por todos os mallocs e free (o que pode ser impossivel em aplicações grandes), até usar ferramentas que ajudam na caça aos memory leaks.

    O que eu faria em qualquer situação (programando em C) seria criar  minhas próprias funções de alocação de desalocação de memória. Na função de alocação eu armazenaria em uma estrutura, informações sobre a memória que está sendo alocada como por exemplo:

    – Nome da variável

    – Arquivo

    – Linha

    – Posição da memória alocada (ÓBVIAMENTE)

    E armazenaria essas informações numa BSTree ou numa Linked list. (O código de busca não precisa ser muito eficiente porque só vai ser usado em modo debug para diagnosticar o problema).

    Na funções de desalocação de memória, usando como chave de busca a posição de memória alocada, basta remover o indice da lista (BSTtree ou Linked List).

    Caso necessário poderiamos tb criar um log num arquivo de todas as alocações e desalocações de memória.

    Após a criação dessas funções, basta criar MACROS para substituir malloc e free em todo o código para usar as funções e para ativar ou desativar o código de detecção de memory leaks.

    Antes do termino do programa, basta checar a lista de alocação de memória e todas os membros dessa lista são memória que deveriam ter sido desalocados e óbviamente não foram.

  3. Marcondes says:

    SINTOMA

    Ao rodar o executável é possível perceber que a medida em que o tempo de execução avança, o programa aumenta o consumo de memória de maneira exponencial. Indício de que há um vazamento de memória (memory leak), ocasionado provavelmente pela alocação de memória sem sua posterior desalocação.

    PROBLEMA

    O programa analisado foi escrito em C. Em C, a forma de alocação de memória disponível para o programador é a função malloc(). A função aloca uma região contígua de memória de tamanho determinado pelo parâmetro informado à função. A função retorna um ponteiro para o início da área alocada. A única forma dessa área de memória ser liberada seria através da finalização do processo raiz do executável, ou então explicitamente pelo programador, através da função free(), à qual se indica o ponteiro da área que se deseja liberar. No programa analisado, é possível observer um loop no qual a cada iteração, um nova área de memória é alocada através da função malloc. Porém, ao final da iteração, é possível observar que a memória alocada não é liberada, uma vez que o a forma de desalocação está incorreta (ponteiro = NULL), de forma que na próxima iteração, a variável utilizada para guardar o ponteiro da área alocada é reutilizada para uma nova alocação, fazendo com que a área anteriormente alocada não seja mais referenciada (raiz do memory leak).

    SOLUÇÃO

    A função "inversa" ao malloc é a função free, em C. Portanto, sem alterar a lógica do programador, é possível corrigir o problema através da substituição do comando pbyAllocated = NULL; por Free(pbyAllocated);

    ESTRATÉGIA

    Rodar o programa executável e monitorar o consumo de memória através do gerenciador de tarefas do windows e dos contadores de performance do windows. Observar a lógica de alocação e desalocação de memória do programa tentando mapear o comportamento esperado do consumo de memória. Se a análise da execução do programa diferir do esperado, há um possível problema de vazamento de memória a ser investigado, entrando mais a fundo no funcionamento do programa.

    MARCONDES

  4. Biel Rezende says:

    Ola Pessoal!

    Não sei se aqui seria o local apropriado para estar falando sobre isso, mas como se trata de um assunto referente ao blog estou colocando meu post aqui mesmo.

    Caso venha estar incomodando ou exista um local adequado para este post peço desculpas ao moderador.

    Gostaria apenas de parabenizar o(s) criador (es) deste blog, tanto pelo seu conteúdo de arquivos, tais como dicas, exemplos, discussões e tutoriais, mas também pela brilhante idéia de terem criado o "DESAFIO DA SEMANA", cujo objetivo do mesmo é lançar um desafio aos usuários do blog e que os mesmos possam trazer a solução fazendo com que passemos por situações as quais geralmente não estamos acostumados a passar e nem mesmo a utilizar certas soluções.

    Não desenvolvo em linguagem C, porém acompanho todos os dias o blog e leio todos os posts independente da linguagem mencionada.

    Terminando então gostaria de mais uma vez pedir desculpas se postei em local errado, mas não poderia deixar de dar minha opinião sobre esta brilhante idéia que os criadores tiveram.

    Parabéns a todos os envolvidos e espero que este blog persista para que possamos a cada dia aprender mais com vocês.

    Um grande abraço, Biel.

  5. Roberto Farah says:

    Oi pessoal!

    Amanha estarei publicando as respostas desse desafio, um novo desafio novo e um artigo sobre DebugDiag.

    Aproveito para fazer uns comentarios sobre os posts acima. :)

    Primeiro, parabens a todos que responderam! As respostas estao corretas!

    Apresentarei, no artigo de respostas, algumas respostas usando os mesmos conceitos.

    Danilo e Marcondes usaram uma abordagem reativa que possibilita checar se ha’ um potencial memory leak, independente da linguagem de programacao usada, sem necessidade de acesso ao fonte! Usamos muito esse conceito em situacoes reais. Marcelo sugeriu uma abordagem proativa onde o vazamento de memoria seria mostrado pela propria aplicacao, incluindo o local ocasionando o leak! Muito bom! Uma aplicacao que tenha implementado esse conceito evitaria abordagens reativas, economizando tempo de troubleshooting, recursos, etc…

    Na pratica costumamos usar ambos os conceitos com aplicacoes que nao tenham mecanismos de prevencao de vazamento de memoria, ou quando suspeitamos que o mecanismo em si tem problemas :) –  Agimos reativamente para comprovar o sintoma e identificar a aplicacao responsavel, depois recomendamos acoes proativas para evitar a ocorrencia do sintoma novamente. Explicarei mais sobre isso no artigo de resposta.

    Biel, muito obrigado pelo post! Continuarei a postar desafios semanais! O proximo sera em VB 6, depois C#, talvez coloque de Assembly tambem.

    Lembrando que o que realmente conta no Desafio da Semana e’ a abordagem para isolar determinado problema e a solucao proposta, uma vez que os problemas serao relativamente simples. Minhas respostas nao devem ser tomadas como unicas, na verdade, tanto os modos de se isolar o problema quando, em muitos casos, as solucoes propostas deveriam ser vistas em termos de vantagens e desvantagens, desde que corretas.

    Obrigado a todos por participar e fiquem a vontade para sugerir artigos/desafios!

    :)

    Roberto Farah