Como eu uso valgrind para encontrar vazamentos de memória?
3 answers
Tenta isto:
valgrind --leak-check=full -v ./your_program
Desde que o valgrind esteja instalado, passará pelo seu programa e dir-lhe-á o que se passa. Ele pode dar-lhe ponteiros e locais aproximados onde suas fugas podem ser encontradas. Se estás em segundo plano, tenta passá-lo.
Como executar Valgrind
Gostaria de construir uma explicação mais descritiva para como usar o Valgrind efetivamente e como resolver vazamentos de memória. Não para insultar a operação, mas para aqueles quem vem a esta questão e ainda é novo no Linux - você pode ter que instale 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 mesmo ser usado para outros línguas quando configuradas correctamente (ver Este para Jiboia).
Para executar valgrind , passe o executável como um argumento (juntamente com qualquer parâmetros do programa).
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
Isto irá produzir um relatório no final da execução do seu programa que (esperemos)
parece isto:
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated
All 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)
Tenho uma fuga, mas onde? Então, tens uma fuga de memória, e o Valgrind não diz nada significativo. Talvez, algo assim:
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 ver o código C que escrevi. too:
#include <stdlib.h>
int main() {
char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
return 0;
}
Bem, perderam-se 5 bytes. Como aconteceu? O relatório de erro só diz
main
e malloc
. Em um programa maior, isso seria seriamente problemático para
cacar. Isto deve-se à forma como o executável foi compilado. Podemos
na verdade, obter detalhes linha-a-linha sobre o que correu mal. Recompilar o seu programa
com uma bandeira de depuração (estou a usar gcc
Aqui):
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 esta compilação de depuração, o Valgrind aponta para a linha exacta do Código alocando a memória que vazou! (A redacção é importante: seja exactamente onde está a tua fuga, mas o que foi que vazou. O rasto ajuda-te 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 depuração de fugas e erros de memória
- faça uso de www.cplusplus.com ! tem uma grande documentação sobre funções C / C++. Conselho Geral para fugas de memória:
- Conselhos Gerais para erros de memória:
- Acede e Escreve a endereços e índices que certamente te pertencem. Memoria
os erros são diferentes das fugas; muitas vezes são apenas
IndexOutOfBoundsException
problemas de tipo.
Não acedas nem escreves à memória depois de a libertares.
- Certifique - se de que a sua memória dinamicamente alocada recebe livrar.
Não alocar memória e esquecer de atribuir o ponteiro.
Não substituas um ponteiro com um novo, a não ser que a memória antiga seja libertada.
- enumere as funções no seu código que dependem/dependem do
código "ofensivo" que tem o erro de memória. Siga a execução do programa
(talvez mesmo em
gdb
talvez), e procure por erros pré-condição/pós-condição.
Tente comentar o bloco de código" ofensivo "(dentro da razão, para que o seu código
ainda compila). Se o erro do Valgrind desaparecer, descobriu onde está.
Um olhar sobre fugas e erros comuns Cuidado com os 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)
E o código:
#include <stdlib.h>
#include <stdint.h>
struct _List {
int32_t* data;
int32_t length;
};
typedef struct _List List;
List* resizeArray(List* array) {
int32_t* dPtr = array->data;
dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
return array;
}
int main() {
List* array = calloc(1, sizeof(List));
array->data = calloc(10, sizeof(int32_t));
array = resizeArray(array);
free(array->data);
free(array);
return 0;
}
Como assistente de Ensino, Já vi este erro muitas vezes. O estudante utiliza
uma variável local e esquece-se de actualizar o ponteiro original. O erro aqui é
notando que realloc
pode realmente mover a memória alocada para outro lugar
e muda a localização do ponteiro. Então partimos sem avisar.
array->data
para onde a matriz foi movida.
Escrita inválida
1 errors in context 1 of 1:
Invalid write of size 1
at 0x4005CA: main (main.c:10)
Address 0x51f905a 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);
return 0;
}
Observe que Valgrind nos aponta para a linha de código comentada acima. Matriz
o tamanho 26 é indexado [0,25], razão pela qual *(alphabet + 26)
é inválido
está fora dos limites. Uma escrita inválida é um resultado comum de
erros "off-by-one". Olha para o lado esquerdo. da sua operação de missão.
Leitura inválida
1 errors in context 1 of 1:
Invalid read of size 1
at 0x400602: main (main.c:9)
Address 0x51f90ba 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);
return 0;
}
Valgrind aponta-nos para a linha comentada acima. Olha para a última iteração aqui.,
que é *(destination + 26) = *(source + 26);
. No entanto, *(source + 26)
fora dos limites novamente, da mesma forma que a escrita inválida. As leituras inválidas são também um
resultado comum de erros "off-by-one". Olha para o lado direito da tua missão.
operacao.
The Open Source (U/Dys)topia
Como é que eu sei quando a fuga o meu é? Como encontro a minha fuga quando estou a usar o código de outra pessoa? Encontrei uma fuga que não é minha. devo fazer alguma coisa? Todo são perguntas legítimas. Primeiro, 2 Exemplos do mundo real que mostram 2 classes de encontros comuns.Jansson : uma biblioteca JSON
#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 pointer
json_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?
return 0;
}
Este é um programa simples: ele lê uma cadeia de JSON e analisa-o. Na criação,
usamos as chamadas da biblioteca para fazer o exame por nós. Jansson faz o necessário
atribuições dinamicamente desde JSON can contém estruturas aninhadas.
No entanto, isso não significa que nós decref
ou "libertemos" a memória que nos foi dada de
todas as funções. Na verdade, este código que escrevi acima lança tanto uma "leitura inválida"
e uma "escrita inválida". Esses erros desaparecem quando você tira a linha decref
para value
.
value
é considerada uma "referência emprestada" no Jansson.
CONJUNTO. O Jansson guarda a memória para ti, e tu tens de ...
JSON estruturas independentes uma da outra. A lição aqui:
leia a documentação . Realmente. Às vezes é difícil de entender, mas ...
estão a dizer-te porque é que estas coisas acontecem. Em vez disso, temos
perguntas existentes sobre este erro de memória.
SDL : uma biblioteca de gráficos e jogos
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Quit();
return 0;
}
Qual é o problema deste código? Ele vaza consistentemente ~ 212 KiB de memória para mim. Pense um pouco. Ligamos o SDL e desligamos. Resposta? Não há nada errado.
Isso pode parecer bizarro no início. Verdade seja dita, os gráficos são bagunçados e às vezes você tem que aceitar algumas vazamentos como sendo parte da biblioteca padrão. A lição aqui: não precisas de apagar todas as fugas de memória . Às vezes, só precisas de suprimir as fugas. porque são problemas conhecidos que não se podem resolver. (Esta não é a minha Permissão para ignorar as suas próprias fugas!)
Respostas para o vazio Como é que eu sei? quando a fuga é minha?É. (99% de certeza, de qualquer forma) Como encontro a minha fuga quando estou a usar o código de outra pessoa?
É provável que alguém já o tenha encontrado. Tenta O Google! Se isso falhar, usa as habilidades que te dei. Se isso falhar e você principalmente Ver chamadas API e pouco de sua própria pilha trace, veja a próxima pergunta. Encontrei uma fuga que não é minha, devo fazer alguma coisa?
É! A maioria das APIs tem maneiras de relatar bugs e problemas. Usa-os! Ajuda a devolver a as ferramentas que você está usando em seu projeto!
Leitura Adicional
Obrigado por ficares comigo tanto tempo. Espero que tenhas aprendido alguma coisa, pois tentei tratar do amplo espectro de pessoas que chegavam a esta resposta. Algumas coisas eu espero que você tenha perguntado ao longo do caminho: como funciona o alocador de memória de C? O que é uma fuga de memória e um erro de memória? Como é que eles são diferentes dos segfaults? Como funciona o Valgrind? Se você tinha algum destes, por favor, alimentar o seu curiosidades:Podes correr:
valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]