Como Utilizar Valgrind para Verificar Memória em C/C++

Uma ferramenta de depuração de memória!


Como Utilizar Valgrind para Verificar Memória em C/C++


Nós já publicamos sobre o GNU GDB que serve para debugar em tempo de execução.

Também já publicamos sobre o CppCheck que serve para fazer análise estática.

Nesse artigo vamos conhecer e saber como utilizar o Valgrind, que é uma ferramenta de programação para depuração de memória, detecção de vazamento de memória e “criação de perfil”.

O Valgrind roda seu código numa máquina virtual, ou seja nada do programa original é executado diretamente no processador! Em vez disso, Valgrind primeiro traduz o programa em um formulário temporário e mais simples chamado representação intermediária (IR), que é um formulário baseado em formulário de atribuição única estático neutro do processador.

Valgrind foi originalmente projetado para ser uma ferramenta de depuração de memória livre para arquiteturas x86, mas desde então evoluiu para se tornar uma estrutura genérica para criar ferramentas de análise dinâmica, como verificadores e criadores de perfil.

Valgrind é, em essência, uma máquina virtual que usa técnicas de compilação just-in-time , incluindo recompilação dinâmica.

Valgrind é um Software Livre licenciado sob os termos da licença: GNU GPLv2.


Instalação

No Windows você vai precisar ter o MinGW instalado(veja aqui como) e depois fazer o download(ou compilar) de acordo com esse procedimento.

Em distribuições GNU/Linux

# Debian, Ubuntu, Mint e similares
sudo apt install valgrind

# Arch, Manjaro e similares
sudo pacman -S valgrind

# Fedora e similares
sudo dnf install valgrind

# No Gentoo, Funtoo e similares
sudo emerge dev-util/valgrind

# Snap
sudo snap install valgrind --classic

Utilização

Suponhamos que você possua o código abaixo que é um mini programa que faz a soma de dois números como parâmetros via linha de comando. O código utiliza Smart Pointers e está tudo funcionando normalmente!

Utilizando Smart Pointers(Pointeiros inteligentes)

#include <iostream>
#include <memory> // PARA SMART POINTERS
#include <algorithm>

struct Math {
  private:
    int num1, num2;
  public:
    Math(const int &inum1, const int &inum2) 
      : num1(inum1), num2(inum2) {}
    int result(){
      return num1 + num2;
    }
};

bool is_number(const std::string& s){
  return !s.empty() && std::find_if(s.begin(), 
      s.end(), [](unsigned char c) { 
        return !std::isdigit(c); 
      }) == s.end();
}

int main(int argc, char **argv){
  if(argc > 2){
    const std::string a = argv[1], b = argv[2];

    if( !is_number(a) || !is_number(b) ){
      std::cerr << "Use only numbers.\n";
      return 1;
    }

    // USANDO SMART POINTERS
    auto math = std::make_shared<Math>(
        std::stoi(a), 
        std::stoi(b)
    );

    std::cout << math->result() << '\n';
  }else{
    std::cerr << "Use: " <<
      argv[0] << " num1 num2\n";
    return 1;
  }
  return 0;
}

Após compilar você roda seu código e resolve testar com Valgrind com o comando e parâmetros:

valgrind -s --leak-check=yes ./a.out 1 2

O Valgrind não detecta nenhuma falha e retorna ao final da saída abaixo: ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0), ou seja, nenhum erro!

==5659== Memcheck, a memory error detector
==5659== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5659== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==5659== Command: ./a.out 1 2
==5659== 
3
==5659== 
==5659== HEAP SUMMARY:
==5659==     in use at exit: 0 bytes in 0 blocks
==5659==   total heap usage: 3 allocs, 3 frees, 73,752 bytes allocated
==5659== 
==5659== All heap blocks were freed -- no leaks are possible
==5659== 
==5659== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Alocando memória

Agora você decide modificar seu código e alocar memória com a declaração new, mas esquece de desalocar com delete. Foi removido o cabeçalho <memory> e substituido o make_shared no código abaixo

#include <iostream>
#include <algorithm>

struct Math {
private:
  int num1, num2;

public:
  Math(const int &inum1, const int &inum2) : num1(inum1), num2(inum2) {}
  int result() { return num1 + num2; }
};

bool is_number(const std::string &s) {
  return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) {
                         return !std::isdigit(c);
                       }) == s.end();
}

int main(int argc, char **argv) {
  if (argc > 2) {
    const std::string a = argv[1], b = argv[2];

    if (!is_number(a) || !is_number(b)) {
      std::cerr << "Use only numbers.\n";
      return 1;
    }

    Math * math = new Math(std::stoi(a), std::stoi(b));
    std::cout << math->result() << '\n';
    // AQUI DEVERIA HAVER O delete PARA DESALOCAR
  } else {
    std::cerr << "Use: " << argv[0] << " num1 num2\n";
    return 1;
  }
  return 0;
}

Além disso, além de compilar sem flags, você inclui todas as possíveis para detecção de erro:

g++ -Wall -Werror -Wextra -Wpedantic main.cpp

Mas, mesmo assim, nenhuma falha foi detectada.

Então, você recolve rodar novamente o Valgrind:

valgrind -s --leak-check=yes ./a.out 1 2

No entanto, dessa vez aparece o erro detectado conforme listado na saída abaixo:

==5857== Memcheck, a memory error detector
==5857== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5857== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==5857== Command: ./a.out 1 2
==5857== 
3
==5857== 
==5857== HEAP SUMMARY:
==5857==     in use at exit: 8 bytes in 1 blocks
==5857==   total heap usage: 3 allocs, 2 frees, 73,736 bytes allocated
==5857== 
==5857== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==5857==    at 0x4845013: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==5857==    by 0x109377: main (in /home/marcos/a.out)
==5857== 
==5857== LEAK SUMMARY:
==5857==    definitely lost: 8 bytes in 1 blocks
==5857==    indirectly lost: 0 bytes in 0 blocks
==5857==      possibly lost: 0 bytes in 0 blocks
==5857==    still reachable: 0 bytes in 0 blocks
==5857==         suppressed: 0 bytes in 0 blocks
==5857== 
==5857== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Note que a Memcheck, uma ferramenta do Valgrind detectou que você esqueceu de desalocar o espaço reservado!

Caso seu Valgrind apareça um erro de saída: Fatal error at startup recomendo você instalar a biblioteca libc6-dbg, por exemplo:

sudo apt install libc6-dbg:i386

A definição da arquitetura i386 é importante!

Para mais informações use o help e o manual:

valgrind --help
man valgrind

memtool cpp linguagemc cppdaily


Compartilhe


Nosso canal no Youtube

Inscreva-se


Marcos Oliveira

Marcos Oliveira

Desenvolvedor de software
https://github.com/terroo


Crie Aplicativos Gráficos para Linux e Windows com C++

Aprenda C++ Moderno e crie Games, Programas CLI, GUI e TUI de forma fácil.

Saiba Mais

Receba as novidades no seu e-mail!

Após cadastro e confirmação do e-mail, enviaremos semanalmente resumos e também sempre que houver novidades por aqui para que você mantenha-se atualizado!