2012-01-05 17 views
10

Valgrind es un excelente depurador de memoria, y tiene la opción de --trace-malloc=yes, que produce algo como esto:Interpretar salida de rastreo-malloc de Valgrind

--16301-- malloc(8) = 0x4EAD748 
--16301-- free(0x4EAD748) 
--16301-- free(0x4EAD498) 
--16301-- malloc(21) = 0x4EAD780 
--16301-- malloc(8) = 0x4EAD838 
--16301-- free(0x4EAD6F8) 
--16301-- calloc(1,88) = 0x4EAD870 
--16301-- realloc(0x0,160)malloc(160) = 0x4EB1CF8 
--16301-- realloc(0x4EB9F28,4) = 0x4EBA060 

¿Existe una herramienta que analiza esta salida y me dice que para cada dirección si no se ha asignado y liberado correctamente en un par coincidente?

GCC tiene algo similar con la función mtrace() y la herramienta de línea de comandos mtrace, pero el formato es diferente.

Pregunta adicional: ¿Es posible dar salida a la dirección real junto a una declaración "definitivamente perdida"?

(estoy etiquetar esta "C" y "C++" para las dos lenguas con más probabilidades de usarse con Valgrind.)

Respuesta

1

de ayer para analizar la salida Obviamente, al ser un C++ pr ogrammer Se supone que debo hacerlo en C++. No había usado std::regex antes y necesito aprender un poco sobre esto primero. Así que aquí es una solución de C++:

#include "boost/regex.hpp" 
#include <functional> 
#include <iostream> 
#include <iterator> 
#include <map> 
#include <stdexcept> 
#include <string> 
#include <vector> 

namespace re = boost; 

long to_long(std::string const& s) 
{ 
    return strtol(s.c_str(), 0, 10); 
} 

template <typename T> 
static void insert(T& map, std::string const& address, std::string const& call, size_t size) 
{ 
    if (!map.insert(std::make_pair(address, std::make_pair(call, size))).second) 
     std::cout << "WARNING: duplicate address for " << call << ": " << address << "\n"; 
} 

template <typename T> 
static void erase(T& map, std::string const& address, std::string const& call) 
{ 
    auto it(map.find(address)); 
    if (it == map.end() && address != "0x0") 
     std::cout << "WARNING: spurious address in " << call << "\n"; 
    else 
     map.erase(it); 
} 

static void process(std::istream& in) 
{ 
    std::map<std::string, std::pair<std::string, size_t>> m; 

    std::vector<std::pair<re::regex, std::function<void(re::smatch&)>>> exps; 
    exps.emplace_back(re::regex(".*(malloc\\((.*)\\)) = (.*)"), [&](re::smatch& results){ 
      ::insert(m, results[3], results[1], ::to_long(results[2])); 
     }); 
    exps.emplace_back(re::regex(".*(free\\((.*)\\))"), [&](re::smatch& results){ 
      ::erase(m, results[2], results[1]); 
     }); 
    exps.emplace_back(re::regex(".*(calloc\\((.*),(.*)\\)) = (.*)"), [&](re::smatch& results){ 
      ::insert(m, results[4], results[1], ::to_long(results[2]) * ::to_long(results[3])); 
     }); 
    exps.emplace_back(re::regex(".*(realloc\\((.*),(.*)\\)) = (.*)"), [&](re::smatch& results){ 
      ::erase(m, results[2], results[1]); 
      ::insert(m, results[4], results[1], ::to_long(results[3])); 
     }); 

    for (std::string line; std::getline(in, line);) 
    { 
     re::smatch results; 
     for (auto it(exps.begin()), end(exps.end()); it != end; ++it) 
     { 
      if (re::regex_match(line, results, it->first)) 
      { 
       (it->second)(results); 
       break; 
      } 
     } 
    } 

    size_t total{0}; 
    for (auto it(m.begin()), end(m.end()); it != end; ++it) 
    { 
     std::cout << "leaked memory at " << it->first << " " << "from " << it->second.first << "\n"; 
     total += it->second.second; 
    } 
    std::cout << "total leak: " << total << "\n"; 
} 

int main(int, char*[]) 
{ 
    try 
    { 
     ::process(std::cin); 
    } 
    catch (std::exception const &ex) 
    { 
     std::cerr << "ERROR: " << ex.what() << "\n"; 
    } 
} 

Porque parece que la versión actual de gcc de std::regex está libre de errores que utiliza la aplicación de Boost. Debería ser fácil cambiar la versión: simplemente defina re como un alias para std en lugar de boost.

3

La salida parece ser una salida parcial (o que es de código horriblemente roto Sin embargo, esto parece ser un trabajo para un simple script de Perl que coincide con la dirección. En realidad, con las expresiones regulares de C++ 2011, incluso C++ debe estar a la altura de la tarea pero aún no las he usado. Entonces, aquí está un simple (aunque probablemente bastante torpe) script de perl lectura de salida de valgrind de la entrada estándar:. perl solución utilizada

#!/usr/bin/perl -w 
use strict; 

my %allocated; 

while (<>) 
    { 
    chomp; 
    if (/(realloc\(([^,]*),([^)]*)\)).* = (.*)/) 
     { 
     if ($2 ne "0x0") 
      { 
      if (!exists $allocated{$2}) 
       { 
       print "spurious realloc($2, $3) = $4\n"; 
       } 
      else 
       { 
       delete $allocated{$2}; 
       } 
      } 
     $allocated{$4} = "$1$;$3"; 
     } 
    elsif (/(malloc\((.*)\)) = (.*)/) 
     { 
     $allocated{$3} = "$1$;$2"; 
     } 
    elsif (/ free\((.*)\)/) 
     { 
     if ($1 ne "0x0") 
      { 
      if (!exists $allocated{$1}) 
       { 
       print "spurious free($1)\n"; 
       } 
      else 
       { 
       delete $allocated{$1}; 
       } 
      } 
     } 
    elsif (/(calloc\((.*),(.*)\)) = (.*)/) 
     { 
     $allocated{$4} = "$1$;" . ($2 * $3); 
     } 
    } 

my $total = 0; 
foreach my $leak (keys %allocated) 
    { 
    my($call, $size) = split(/$;/, $allocated{$leak}); 
    print "leak: address=$leak source=$call size=$size\n"; 
    $total += $size; 
    } 

if (0 < $total) 
    { 
    print "total leak=$total\n"; 
    } 
+0

Sí, ¡la salida es solo un ejemplo para demostrar las posibles líneas! Déjame probar el guión, ¡gracias! Oh, ¿puedes hacer que ignore todas las líneas que no se ajustan al patrón? –

+0

Muy agradable. Una pregunta: ¿Esto explica las diversas posibilidades de 'realloc'? Puede ser un nuevo 'malloc' directamente o puede mover una dirección existente. (Además, 'libre (0)' no es falso :-).) –

+0

Lamentamos ser una plaga: ¿puedes hacer que sume la cantidad filtrada final? Quiero compararlo con el propio informe de Valgrind. –

1

Llego un poco tarde a la fiesta, pero la otra respuesta no me tomó en cuenta. Hay otras funciones como valloc, cfree o posix_memalign, pero al menos en Linux tienen alias. De todos modos aquí está mi versión de Python, sin garantías.

#!/usr/bin/python 
import sys, re 

memmap = {} 

for line in sys.stdin: 
    tok = [x for x in re.split(' |\(|\)|,|=|\n', line) if x][1:] 
    if tok and tok[0] in ['malloc', 'calloc', 'memalign', 'realloc', 'free']: 
     addr = int(tok[-1], 16) 
     if tok[0] == 'malloc': 
      memmap[addr] = int(tok[1]) 
     elif tok[0] == 'calloc': 
      memmap[addr] = int(tok[1]) * int(tok[2]) 
     elif tok[0] == 'memalign': 
      memmap[addr] = int(tok[-2]) 
     elif tok[0] == 'realloc': 
      oldaddr = int(tok[1], 16) 
      if oldaddr != 0: 
       del memmap[oldaddr] 
      memmap[addr] = int(tok[2]) 
     elif tok[0] == 'free' and addr != 0: 
      del memmap[addr] 

for k, v in memmap.iteritems(): 
    print 'leak at 0x%x, %d bytes' % (k, v) 
print 'total %d bytes' % sum(memmap.itervalues())