2012-01-05 80 views
10

Valgrind是一個優秀的內存調試器,它具有選項--trace-malloc=yes,產生這樣的事情:解釋Valgrind的的跟蹤malloc的輸出

--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」和「C++」爲這兩種語言最有可能與Valgrind的使用。)

回答

1

昨天的溶液中使用的Perl分析輸出顯然,是一個C++ PR程序員我應該用C++來完成它。我之前沒有使用過std::regex,需要先了解一下。因此,這裏是一個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"; 
    } 
} 

因爲它似乎是海灣合作委員會目前的std::regex版本是越野車我使用加速實施。切換版本應該很容易:只需將re定義爲std的別名,而不是boost

3

輸出似乎是一個局部輸出(或者它是從可怕斷碼。但是,這似乎是一個簡單的Perl腳本匹配地址的工作。事實上,使用C++ 2011的正則表達式,甚至C++應該能夠勝任這項任務,但我還沒有使用它們。一個簡單的(雖然可能相當笨拙)perl腳本讀取來自標準輸入的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"; 
    } 
+0

是的,輸出只是一個示例來演示可能的行!讓我試試這個劇本 - 謝謝!哦,你能否讓它忽略所有不符合該模式的線? – 2012-01-06 00:14:35

+0

非常好。一個問題:這是否說明了'realloc'的各種可能性?它可以是一個新的'malloc',也可以移動一個現有的地址。 (另外,'free(0)'不是虛假的:-)。) – 2012-01-06 00:37:18

+0

對不起,您可以將最終的泄漏量加起來嗎?我想比較一下Valgrind自己的報告。 – 2012-01-06 00:39:22

1

我對晚會有點遲,但其他答案沒有考慮memalign。還有其他的功能,如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())