Como eu uso valgrind para encontrar vazamentos de memória?

Como é que uso o valgrind para encontrar fugas de memória num programa?

Por favor, alguém me ajude e descreva os passos para realizar o procedimento?

Estou a usar o Ubuntu 10.04 e tenho um programa, por favor ajuda-me.

 73
Author: Tony, 2011-02-27

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.

 115
Author: RageD, 2016-12-16 22:23:03

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:
      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.
  • 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.
  • Às Vezes Valgrind nem sempre aponta para onde está o teu erro. Às vezes você tem um monte de erros e resolver um deles resolve uma cascata de outros.
    • 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á.
  • Se tudo o resto falhar, tenta procurar. Valgrind tem documentação também!
    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.

    Porquê? A variável 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:
     72
    Author: Josh Detwiler, 2018-07-18 03:00:38

    Podes correr:

    valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]
    
     23
    Author: Rajat Paliwal, 2014-10-06 13:13:54