Интерпретировать вывод Valgrind trace-malloc

Valgrind - отличный отладчик памяти, и у него есть опция --trace-malloc=yes, которая производит что-то вроде этого:

--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

Есть ли инструмент, который анализирует этот вывод и сообщает мне для каждого адреса, правильно ли он был выделен и освобожден в соответствующей паре?

GCC имеет нечто похожее с функцией mtrace() и инструментом командной строки mtrace, но формат отличается.

Бонусный вопрос: возможно ли вывести фактический адрес рядом с выражением "определенно потерянный"?

(Я отмечаю эти "C" и "С++" для двух языков, которые, скорее всего, будут использоваться с Valgrind.)

Ответ 1

Вчера решение использовало perl для анализа вывода. Очевидно, что я программист на С++, я должен делать это на С++. Раньше я не использовал std::regex, и сначала нужно немного узнать об этом. Итак, вот С++-решение:

#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";
    }
}

Потому что кажется, что gcc текущая версия std::regex багги я использовал реализацию от Boost. Переключение версии должно быть легко: просто определите re как псевдоним для std вместо boost.

Ответ 2

Выход кажется частичным выходом (или это из-за отвратительно сломанного кода). Однако это похоже на работу по простому perl script, соответствующему адресу. На самом деле, с регулярными выражениями С++ 2011 С++ должен соответствовать задаче, но я еще не использовал их. Итак, вот простой (хотя, вероятно, довольно неуклюжий) perl script выводящий valgrind вывод со стандартного ввода:

 #!/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";
  }

Ответ 3

Я немного опоздал на вечеринку, но другой ответ не принял во внимание. Существуют и другие функции, такие как valloc, cfree или posix_memalign, но по крайней мере на linux они псевдонимы. В любом случае, вот моя версия python, никаких гарантий.

#!/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())