|  | #!/usr/bin/env python | 
|  | # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Symbolizes and prints live objects as recorded by tcmalloc's | 
|  | HeapProfilerDumpLiveObjects. | 
|  | """ | 
|  |  | 
|  | import os | 
|  | import re | 
|  | import subprocess | 
|  | import sys | 
|  | import tempfile | 
|  |  | 
|  | def usage(): | 
|  | print """\ | 
|  | Usage: | 
|  | tools/tcmalloc/print-live-objects.py out/Debug/chrome leaks.dmp | 
|  | """ | 
|  |  | 
|  | def LoadDump(dump_file): | 
|  | result = [] | 
|  | leakfmt = re.compile( | 
|  | r"^\s*1:\s*(\d+)\s*\[\s*1:\s*\d+\]\s*@(0x[a-f0-9]+)((\s+0x[a-f0-9]+)*)$") | 
|  | line_no = 0 | 
|  | with open(dump_file) as f: | 
|  | for line in f: | 
|  | line_no = line_no + 1 | 
|  | matches = leakfmt.match(line) | 
|  | if not matches: | 
|  | print "%s: could not parse line %d, skipping" % (dump_file, line_no) | 
|  | else: | 
|  | trace = { "size": int(matches.group(1)), | 
|  | "address": matches.group(2), | 
|  | "frames": matches.group(3).strip().split(" ")} | 
|  | result.append(trace) | 
|  | return result | 
|  |  | 
|  |  | 
|  | def Symbolize(binary, traces): | 
|  | addresses = set() | 
|  | for trace in traces: | 
|  | for frame in trace["frames"]: | 
|  | addresses.add(frame) | 
|  | addr_file, addr_filename = tempfile.mkstemp() | 
|  | for addr in addresses: | 
|  | os.write(addr_file, "%s\n" % addr) | 
|  | os.close(addr_file) | 
|  | syms = subprocess.Popen([ | 
|  | "addr2line", "-f", "-C", "-e", binary, "@%s" % addr_filename], | 
|  | stdout=subprocess.PIPE).communicate()[0].strip().split("\n") | 
|  | table = {} | 
|  | cwd = os.getcwd() | 
|  | for address, symbol, location in zip(addresses, syms[::2], syms[1::2]): | 
|  | if location != "??:0": | 
|  | filename, line = location.split(":") | 
|  | filename = os.path.realpath(filename)[len(cwd)+1:] | 
|  | location = "%s:%s" % (filename, line) | 
|  | table[address] = { "name": symbol, "location": location } | 
|  | for trace in traces: | 
|  | frames = [] | 
|  | for frame in trace["frames"]: | 
|  | frames.append(table[frame]) | 
|  | trace["frames"] = frames | 
|  |  | 
|  |  | 
|  | def Main(argv): | 
|  | if sys.platform != 'linux2': | 
|  | print 'print-live-objects.py requires addr2line only present on Linux.' | 
|  | sys.exit(1) | 
|  |  | 
|  | if len(argv) != 3: | 
|  | usage() | 
|  | sys.exit(1) | 
|  |  | 
|  | traces = LoadDump(argv[2]) | 
|  | Symbolize(argv[1], traces) | 
|  |  | 
|  | if not traces: | 
|  | print "No leaks found!" | 
|  |  | 
|  | for trace in sorted(traces, key=lambda x: -x["size"]): | 
|  | print "Leak of %d bytes at address %s" % (trace["size"], trace["address"]) | 
|  | for frame in trace["frames"]: | 
|  | print "  %s (%s)" % (frame["name"], frame["location"]) | 
|  | print "" | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | Main(sys.argv) |