Você usa o valgrind para testar seu programa compilado , não o código fonte.
22411 Tony
6
A resposta dada abaixo por @RageD está correta. Por que você não a aceita?
Pratik Singhal
1
Um vazamento é causado por algo que você deixa de fazer - ou seja. liberte memória alocada. Portanto, o Valgrind não pode mostrar "onde" está o vazamento - apenas você sabe onde a memória alocada não é mais necessária. No entanto, informando qual alocação não está sendo livre () d, rastreando o uso dessa memória por meio do seu programa, você poderá determinar onde ela deve ficar livre () d. Um erro comum é sair de uma função sem liberar memória alocada.
Não para insultar o OP, mas para aqueles que chegam a essa pergunta e ainda são novos no Linux - talvez seja necessário instalar o Valgrind no seu sistema.
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
O Valgrind é facilmente utilizável para código C / C ++, mas pode até ser usado para outras linguagens quando configurado corretamente (veja isto em Python).
Para executar o Valgrind , passe o executável como argumento (junto com quaisquer parâmetros ao programa).
--leak-check=full: "cada vazamento individual será mostrado em detalhes"
--show-leak-kinds=all: Mostre todos os tipos de vazamento "definidos, indiretos, possíveis e acessíveis" no relatório "completo".
--track-origins=yes: Favorece a saída útil acima da velocidade. Isso rastreia as origens de valores não inicializados, o que pode ser muito útil para erros de memória. Considere desligar se Valgrind estiver inaceitavelmente lento.
--verbose: Pode falar sobre o comportamento incomum do seu programa. Repita para mais detalhes.
--log-file: Escreva para um arquivo. Útil quando a saída excede o espaço do terminal.
Por fim, você gostaria de ver um relatório Valgrind parecido com este:
HEAP SUMMARY:
in use at exit:0 bytes in 0 blocks
total heap usage:636 allocs,636 frees,25,393 bytes allocatedAll heap blocks were freed -- no leaks are possible
ERROR SUMMARY:0 errors from 0 contexts (suppressed:0 from 0)
ERROR SUMMARY:0 errors from 0 contexts (suppressed:0 from 0)
Eu tenho um vazamento, mas ONDE ?
Então, você tem um vazamento de memória e Valgrind não está dizendo nada significativo. Talvez algo como isto:
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
Vamos dar uma olhada no código C que escrevi também:
#include<stdlib.h>int main(){char* string = malloc(5*sizeof(char));//LEAK: not freed!return0;}
Bem, houve 5 bytes perdidos. Como isso aconteceu? O relatório de erro apenas diz
maine malloc. Em um programa maior, isso seria seriamente problemático de caçar. Isso ocorre devido à forma como o executável foi compilado . Na verdade, podemos obter detalhes linha por linha sobre o que deu errado. Recompile seu programa com um sinalizador de depuração (estou usando gccaqui):
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall-ggdb3 main.c # add -ggdb3 to it
Agora, com essa compilação de depuração, Valgrind aponta para a linha exata de código que
aloca a memória que vazou! (A redação é importante: pode não ser exatamente onde está o vazamento, mas o que vazou. O rastreamento ajuda a encontrar
onde .)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
Técnicas para depurar vazamentos de memória e erros
Faça uso de www.cplusplus.com ! Possui excelente documentação sobre funções C / C ++.
Conselho geral para vazamentos de memória:
Verifique se a memória alocada dinamicamente é liberada.
Não aloque memória e esqueça de atribuir o ponteiro.
Não substitua um ponteiro por um novo, a menos que a memória antiga seja liberada.
Conselho geral para erros de memória:
Acesse e escreva para endereços e índices que certamente pertencem a você. Erros de memória são diferentes de vazamentos; geralmente são apenas IndexOutOfBoundsException
problemas de digitação.
Não acesse ou grave na memória depois de liberá-la.
Às vezes, seus vazamentos / erros podem ser vinculados um ao outro, como um IDE descobrindo que você ainda não digitou um colchete. A resolução de um problema pode resolver outros, portanto, procure um que pareça um bom culpado e aplique algumas dessas idéias:
Liste as funções no seu código que dependem / são dependentes do código "ofensivo" que possui o erro de memória. Siga a execução do programa (talvez até gdbtalvez) e procure por erros de pré-condição / pós-condição. A idéia é rastrear a execução do seu programa enquanto se concentra no tempo de vida da memória alocada.
Tente comentar o bloco de código "ofensivo" (dentro do razoável, para que seu código ainda seja compilado). Se o erro do Valgrind desaparecer, você encontrou onde está.
Se tudo mais falhar, tente procurar. Valgrind também tem documentação !
Um olhar sobre vazamentos e erros comuns
Assista seus ponteiros
60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
Como assistente de ensino, já vi esse erro com frequência. O aluno utiliza uma variável local e esquece de atualizar o ponteiro original. O erro aqui é perceber que reallocrealmente pode mover a memória alocada para outro lugar e alterar a localização do ponteiro. Em seguida, partimos resizeArraysem dizer para
array->dataonde a matriz foi movida.
Gravação inválida
1 errors in context 1 of 1:Invalid write of size 1
at 0x4005CA: main (main.c:10)Address0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
E o código:
#include<stdlib.h>#include<stdint.h>int main(){char* alphabet = calloc(26,sizeof(char));for(uint8_t i =0; i <26; i++){*(alphabet + i)='A'+ i;}*(alphabet +26)='\0';//null-terminate the string?
free(alphabet);return0;}
Observe que Valgrind nos aponta a linha de código comentada acima. A matriz de tamanho 26 é indexada [0,25] e é por isso que *(alphabet + 26)uma gravação inválida está fora dos limites. Uma gravação inválida é um resultado comum de erros de um por um. Olhe para o lado esquerdo da sua operação de atribuição.
Leitura inválida
1 errors in context 1 of 1:Invalid read of size 1
at 0x400602: main (main.c:9)Address0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
E o código:
#include<stdlib.h>#include<stdint.h>int main(){char* destination = calloc(27,sizeof(char));char* source = malloc(26*sizeof(char));for(uint8_t i =0; i <27; i++){*(destination + i)=*(source + i);//Look at the last iteration.}
free(destination);
free(source);return0;}
Valgrind nos aponta para a linha comentada acima. Veja a última iteração aqui, que é *(destination + 26) = *(source + 26);. No entanto, *(source + 26)está fora dos limites novamente, da mesma forma que a gravação inválida. Leituras inválidas também são um resultado comum de erros off-by-one. Olhe para o lado direito da sua operação de atribuição.
A topia de código aberto (U / Dys)
Como sei quando o vazamento é meu? Como encontro meu vazamento quando estou usando o código de outra pessoa? Encontrei um vazamento que não é meu; devo fazer alguma coisa? Todos são perguntas legítimas. Primeiro, 2 exemplos do mundo real que mostram 2 classes de encontros comuns.
#include<jansson.h>#include<stdio.h>int main(){char* string ="{ \"key\": \"value\" }";json_error_t error;json_t* root = json_loads(string,0,&error);//obtaining a pointerjson_t* value = json_object_get(root,"key");//obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value));//use value
json_decref(value);//Do I free this pointer?
json_decref(root);//What about this one? Does the order matter?return0;}
Este é um programa simples: lê uma string JSON e a analisa. Ao fazer isso, usamos chamadas de biblioteca para fazer a análise para nós. Jansson faz as alocações necessárias dinamicamente, já que o JSON pode conter estruturas aninhadas. No entanto, isso não significa que nós decrefou "liberamos" a memória que nos é fornecida de todas as funções. De fato, este código que escrevi acima gera uma "leitura inválida" e uma "gravação inválida". Esses erros desaparecem quando você retira a decreflinha para value.
Por quê? A variável valueé considerada uma "referência emprestada" na API Jansson. A Jansson mantém o controle de sua memória para você, e você simplesmente precisa decref
estruturar o JSON independentemente uma da outra. A lição aqui:
leia a documentação . Realmente. Às vezes é difícil de entender, mas eles estão lhe dizendo por que essas coisas acontecem. Em vez disso, temos
perguntas existentes sobre esse erro de memória.
O que há de errado com este código ? Ele sempre vaza ~ 212 KiB de memória para mim. Tome um momento para pensar sobre isso. Ativamos e desativamos o SDL. Responda? Não há nada errado.
Isso pode parecer bizarro no começo . Verdade seja dita, os gráficos são confusos e às vezes você precisa aceitar alguns vazamentos como parte da biblioteca padrão. A lição aqui: você não precisa reprimir todos os vazamentos de memória . Às vezes, você só precisa suprimir os vazamentos, porque são problemas conhecidos dos quais você não pode fazer nada . (Esta não é minha permissão para ignorar seus próprios vazamentos!)
Respostas ao vazio
Como sei quando o vazamento é meu?
Isto é. (99% de certeza)
Como encontro meu vazamento quando estou usando o código de outra pessoa?
Provavelmente, alguém já o encontrou. Experimente o Google! Se isso falhar, use as habilidades que eu lhe dei acima. Se isso falhar e você vir principalmente chamadas de API e pouco de seu próprio rastreamento de pilha, consulte a próxima pergunta.
Encontrei um vazamento que não é meu; devo fazer alguma coisa?
Sim! A maioria das APIs tem maneiras de relatar bugs e problemas. Usa-os! Ajude a devolver as ferramentas que você está usando no seu projeto!
Leitura adicional
Obrigado por ficar comigo por tanto tempo. Espero que você tenha aprendido alguma coisa, pois tentei atender ao amplo espectro de pessoas que chegam a essa resposta. Espero que você tenha perguntado algumas coisas ao longo do caminho: Como o alocador de memória de C funciona? O que realmente é um vazamento de memória e um erro de memória? Como eles são diferentes de segfaults? Como funciona o Valgrind? Se você teve algum destes, alimente sua curiosidade:
Resposta muito melhor, uma pena que esta não seja a resposta aceita.
A. Smoliak
Eu acredito que é uma boa prática fazer tal coisa, eu mesmo fiz algumas #
A. Smoliak 27/03/18
1
Posso marcar esta resposta com estrela e usá-la como referência futura para mim? Bom trabalho!
Zap
A memcheckferramenta está ativada por padrão?
abhiarora 03/04
@abhiarora Sim. A página do manual nos diz que memchecké a ferramenta padrão:--tool=<toolname> [default: memcheck]
Joshua Detwiler
146
Tente o seguinte:
valgrind --leak-check=full -v ./your_program
Enquanto o valgrind estiver instalado, ele percorrerá o seu programa e dirá o que há de errado. Pode fornecer dicas e locais aproximados onde seus vazamentos podem ser encontrados. Se você estiver fazendo uma segfault, tente executá-lo gdb.
Respostas:
Como executar o Valgrind
Não para insultar o OP, mas para aqueles que chegam a essa pergunta e ainda são novos no Linux - talvez seja necessário instalar o Valgrind no seu sistema.
O Valgrind é facilmente utilizável para código C / C ++, mas pode até ser usado para outras linguagens quando configurado corretamente (veja isto em Python).
Para executar o Valgrind , passe o executável como argumento (junto com quaisquer parâmetros ao programa).
As bandeiras são, em resumo:
--leak-check=full
: "cada vazamento individual será mostrado em detalhes"--show-leak-kinds=all
: Mostre todos os tipos de vazamento "definidos, indiretos, possíveis e acessíveis" no relatório "completo".--track-origins=yes
: Favorece a saída útil acima da velocidade. Isso rastreia as origens de valores não inicializados, o que pode ser muito útil para erros de memória. Considere desligar se Valgrind estiver inaceitavelmente lento.--verbose
: Pode falar sobre o comportamento incomum do seu programa. Repita para mais detalhes.--log-file
: Escreva para um arquivo. Útil quando a saída excede o espaço do terminal.Por fim, você gostaria de ver um relatório Valgrind parecido com este:
Eu tenho um vazamento, mas ONDE ?
Então, você tem um vazamento de memória e Valgrind não está dizendo nada significativo. Talvez algo como isto:
Vamos dar uma olhada no código C que escrevi também:
Bem, houve 5 bytes perdidos. Como isso aconteceu? O relatório de erro apenas diz
main
emalloc
. Em um programa maior, isso seria seriamente problemático de caçar. Isso ocorre devido à forma como o executável foi compilado . Na verdade, podemos obter detalhes linha por linha sobre o que deu errado. Recompile seu programa com um sinalizador de depuração (estou usandogcc
aqui):Agora, com essa compilação de depuração, Valgrind aponta para a linha exata de código que aloca a memória que vazou! (A redação é importante: pode não ser exatamente onde está o vazamento, mas o que vazou. O rastreamento ajuda a encontrar onde .)
Técnicas para depurar vazamentos de memória e erros
IndexOutOfBoundsException
problemas de digitação.Às vezes, seus vazamentos / erros podem ser vinculados um ao outro, como um IDE descobrindo que você ainda não digitou um colchete. A resolução de um problema pode resolver outros, portanto, procure um que pareça um bom culpado e aplique algumas dessas idéias:
gdb
talvez) e procure por erros de pré-condição / pós-condição. A idéia é rastrear a execução do seu programa enquanto se concentra no tempo de vida da memória alocada.Um olhar sobre vazamentos e erros comuns
Assista seus ponteiros
E o código:
Como assistente de ensino, já vi esse erro com frequência. O aluno utiliza uma variável local e esquece de atualizar o ponteiro original. O erro aqui é perceber que
realloc
realmente pode mover a memória alocada para outro lugar e alterar a localização do ponteiro. Em seguida, partimosresizeArray
sem dizer paraarray->data
onde a matriz foi movida.Gravação inválida
E o código:
Observe que Valgrind nos aponta a linha de código comentada acima. A matriz de tamanho 26 é indexada [0,25] e é por isso que
*(alphabet + 26)
uma gravação inválida está fora dos limites. Uma gravação inválida é um resultado comum de erros de um por um. Olhe para o lado esquerdo da sua operação de atribuição.Leitura inválida
E o código:
Valgrind nos aponta para a linha comentada acima. Veja a última iteração aqui, que é
*(destination + 26) = *(source + 26);
. No entanto,*(source + 26)
está fora dos limites novamente, da mesma forma que a gravação inválida. Leituras inválidas também são um resultado comum de erros off-by-one. Olhe para o lado direito da sua operação de atribuição.A topia de código aberto (U / Dys)
Como sei quando o vazamento é meu? Como encontro meu vazamento quando estou usando o código de outra pessoa? Encontrei um vazamento que não é meu; devo fazer alguma coisa? Todos são perguntas legítimas. Primeiro, 2 exemplos do mundo real que mostram 2 classes de encontros comuns.
Jansson : uma biblioteca JSON
Este é um programa simples: lê uma string JSON e a analisa. Ao fazer isso, usamos chamadas de biblioteca para fazer a análise para nós. Jansson faz as alocações necessárias dinamicamente, já que o JSON pode conter estruturas aninhadas. No entanto, isso não significa que nós
decref
ou "liberamos" a memória que nos é fornecida de todas as funções. De fato, este código que escrevi acima gera uma "leitura inválida" e uma "gravação inválida". Esses erros desaparecem quando você retira adecref
linha paravalue
.Por quê? A variável
value
é considerada uma "referência emprestada" na API Jansson. A Jansson mantém o controle de sua memória para você, e você simplesmente precisadecref
estruturar o JSON independentemente uma da outra. A lição aqui: leia a documentação . Realmente. Às vezes é difícil de entender, mas eles estão lhe dizendo por que essas coisas acontecem. Em vez disso, temos perguntas existentes sobre esse erro de memória.SDL : uma biblioteca de gráficos e jogos
O que há de errado com este código ? Ele sempre vaza ~ 212 KiB de memória para mim. Tome um momento para pensar sobre isso. Ativamos e desativamos o SDL. Responda? Não há nada errado.
Isso pode parecer bizarro no começo . Verdade seja dita, os gráficos são confusos e às vezes você precisa aceitar alguns vazamentos como parte da biblioteca padrão. A lição aqui: você não precisa reprimir todos os vazamentos de memória . Às vezes, você só precisa suprimir os vazamentos, porque são problemas conhecidos dos quais você não pode fazer nada . (Esta não é minha permissão para ignorar seus próprios vazamentos!)
Respostas ao vazio
Como sei quando o vazamento é meu?
Isto é. (99% de certeza)
Como encontro meu vazamento quando estou usando o código de outra pessoa?
Provavelmente, alguém já o encontrou. Experimente o Google! Se isso falhar, use as habilidades que eu lhe dei acima. Se isso falhar e você vir principalmente chamadas de API e pouco de seu próprio rastreamento de pilha, consulte a próxima pergunta.
Encontrei um vazamento que não é meu; devo fazer alguma coisa?
Sim! A maioria das APIs tem maneiras de relatar bugs e problemas. Usa-os! Ajude a devolver as ferramentas que você está usando no seu projeto!
Leitura adicional
Obrigado por ficar comigo por tanto tempo. Espero que você tenha aprendido alguma coisa, pois tentei atender ao amplo espectro de pessoas que chegam a essa resposta. Espero que você tenha perguntado algumas coisas ao longo do caminho: Como o alocador de memória de C funciona? O que realmente é um vazamento de memória e um erro de memória? Como eles são diferentes de segfaults? Como funciona o Valgrind? Se você teve algum destes, alimente sua curiosidade:
malloc
o alocador de memória de Cfonte
memcheck
ferramenta está ativada por padrão?memcheck
é a ferramenta padrão:--tool=<toolname> [default: memcheck]
Tente o seguinte:
valgrind --leak-check=full -v ./your_program
Enquanto o valgrind estiver instalado, ele percorrerá o seu programa e dirá o que há de errado. Pode fornecer dicas e locais aproximados onde seus vazamentos podem ser encontrados. Se você estiver fazendo uma segfault, tente executá-lo
gdb
.fonte
your_program
== o nome do executável ou qualquer comando que você usa para executar seu aplicativo.Você pode correr:
fonte
Você pode criar um alias no arquivo .bashrc da seguinte maneira
Portanto, sempre que quiser verificar vazamentos de memória, basta fazer
Isso irá gerar um arquivo de log Valgrind no diretório atual.
fonte