Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 126 lines (111 sloc) 4.549 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
#!/usr/bin/python
# -*- encoding: windows-1250 -*-

"""Annotate memleaks.log from mmgr that have a numerical backtrace with text backtrace.

Alloc. Addr Size Addr Size BreakOn BreakOn
Number Reported Reported Actual Actual Unused Method Dealloc Realloc Allocated by Backtrace (optional)
------ ---------- ---------- ---------- ---------- ---------- -------- ------- ------- -----------------------------------------------------------------------------------------------------------
012345 0x0D200530 0x00000008 0x0D2004B0 0x00000108 0x00000000 new N N ??(00000)::?? 004834da 00535d62 004cef1f 004d7fba 004d8617 007c9c5e 00804a5d 0042749a 0042ef34 0041e28d"""

import os, sys
from subprocess import Popen, PIPE
import re

__author__ = "Marek Baczyñski <imbaczek@gmail.com>"
__license__ = "GPLv2"

CMD_GDB = "gdb -q %s"
CMD_ADDR2LINE = "addr2line -f -i -e %s"
CMD_CPPFILT = "c++filt"

def call_cppfilt(sym):
    p = Popen((CMD_CPPFILT).split(), bufsize=1024,
            stdin=PIPE, stdout=PIPE)
    stdout, stderr = p.communicate(sym)
    return stdout

cache_hits = 0
def try_in_cache(bt, cache):
    global cache_hits
    result = []
    for addr in bt:
        if addr not in cache:
            return None
        else:
            result.append(cache[addr])
    cache_hits += 1
    return result

_addr2linecache = {}
def process_bt_addr2line(bt):
    cached = try_in_cache(bt, _addr2linecache)
    if cached:
        return cached
    addr2line = Popen((CMD_ADDR2LINE%sys.argv[1]).split(), bufsize=1024,
            stdin=PIPE, stdout=PIPE)
    stdout, stderr = addr2line.communicate('\n'.join(bt))
    lines = stdout.splitlines()
    result = []
    for i in xrange(0, len(lines), 2):
        sym = lines[i]
        fileline = lines[i+1]
        if sym != "??" and sym.startswith('_Z'):
            sym = call_cppfilt('_'+sym)
        result.append((bt[i//2], fileline, sym))
        _addr2linecache[bt[i//2]] = result[-1]
    return result

_gdbcache = {}
_regdb = re.compile(r'^Line (?P<line>\d+) of "(?P<filename>[\w\\/\.\-+:]*)" starts at address\s+0x[0-9a-f]+ <(?P<sym>\w+)\+\d+> and ends at 0x[0-9a-f]+ <(?P<symend>[^+]+)(\+\d+)?>.')
_regdb_noinfo = re.compile(r'^No line number information available for address\s+0x[0-9a-f]+ <(?P<sym>\w+)\+\d+>')
def process_bt_gdb(bt):
    cached = try_in_cache(bt, _gdbcache)
    if cached:
        return cached
    commands = ['info line *%s'%addr for addr in bt]
    gdb = Popen((CMD_GDB%sys.argv[1]).split() + ['--batch'] + \
                ['--eval-command=%s'%cmd for cmd in commands],
            bufsize=1024, stdin=PIPE, stdout=PIPE)
    stdout, stderr = gdb.communicate()
    lines = []
    for line in stdout.splitlines():
        if line.startswith((' ', '\t')):
            lines[-1] += " "+line.strip()
        else:
            lines.append(line)
    result = []
    for i, line in enumerate(lines):
        m = _regdb.match(line)
        if m:
            fileline = "%s:%s"%(m.group('filename'), m.group('line'))
        else:
            m = _regdb_noinfo.match(line)
            fileline = "????:??"
        if not m:
            print '\t%s'%line
            continue
        sym = m.group('sym')
        if sym != "??" and sym.startswith('_Z'):
            sym = call_cppfilt('_'+sym)
        result.append((bt[i], fileline, sym))
        _gdbcache[bt[i]] = result[-1]
    return result


def parse_line(line, usegdb=False):
    if not line.startswith(tuple('0123456789')):
        return False
    if "memory leaks found:" in line:
        return False
    fields = line.split()
    bt = [hex(int(f, 16)-4) for f in fields[10:]]
    if not usegdb:
        for addr, fileline, sym in process_bt_addr2line(bt):
            print '\t%-12s %s\t\t%s'%(addr, fileline, sym)
    else:
        for addr, fileline, sym in process_bt_gdb(bt):
            print '\t%-12s %-40s %s'%(addr, fileline, sym)
    return True


def main():
    addr2line = Popen((CMD_ADDR2LINE%sys.argv[1]).split(), bufsize=1024,
            stdin=PIPE, stdout=PIPE)
    count = 0
    for line in file('memleaks.log'):
        print line.strip()
        if parse_line(line, usegdb=True):
            count += 1
            print
    print '%s total lines annotated'%count
    print '%s cache hits'%cache_hits


if __name__ == '__main__':
    main()
Something went wrong with that request. Please try again.