1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Symbolizes and prints live objects as recorded by tcmalloc's
7HeapProfilerDumpLiveObjects.
8"""
9
10import os
11import re
12import subprocess
13import sys
14import tempfile
15
16def usage():
17  print """\
18Usage:
19  tools/tcmalloc/print-live-objects.py out/Debug/chrome leaks.dmp
20"""
21
22def LoadDump(dump_file):
23  result = []
24  leakfmt = re.compile(
25      r"^\s*1:\s*(\d+)\s*\[\s*1:\s*\d+\]\s*@(0x[a-f0-9]+)((\s+0x[a-f0-9]+)*)$")
26  line_no = 0
27  with open(dump_file) as f:
28    for line in f:
29      line_no = line_no + 1
30      matches = leakfmt.match(line)
31      if not matches:
32        print "%s: could not parse line %d, skipping" % (dump_file, line_no)
33      else:
34        trace = { "size": int(matches.group(1)),
35                  "address": matches.group(2),
36                  "frames": matches.group(3).strip().split(" ")}
37        result.append(trace)
38  return result
39
40
41def Symbolize(binary, traces):
42  addresses = set()
43  for trace in traces:
44    for frame in trace["frames"]:
45      addresses.add(frame)
46  addr_file, addr_filename = tempfile.mkstemp()
47  for addr in addresses:
48    os.write(addr_file, "%s\n" % addr)
49  os.close(addr_file)
50  syms = subprocess.Popen([
51      "addr2line", "-f", "-C", "-e", binary, "@%s" % addr_filename],
52      stdout=subprocess.PIPE).communicate()[0].strip().split("\n")
53  table = {}
54  cwd = os.getcwd()
55  for address, symbol, location in zip(addresses, syms[::2], syms[1::2]):
56    if location != "??:0":
57      filename, line = location.split(":")
58      filename = os.path.realpath(filename)[len(cwd)+1:]
59      location = "%s:%s" % (filename, line)
60    table[address] = { "name": symbol, "location": location }
61  for trace in traces:
62    frames = []
63    for frame in trace["frames"]:
64      frames.append(table[frame])
65    trace["frames"] = frames
66
67
68def Main(argv):
69  if sys.platform != 'linux2':
70    print 'print-live-objects.py requires addr2line only present on Linux.'
71    sys.exit(1)
72
73  if len(argv) != 3:
74    usage()
75    sys.exit(1)
76
77  traces = LoadDump(argv[2])
78  Symbolize(argv[1], traces)
79
80  if not traces:
81    print "No leaks found!"
82
83  for trace in sorted(traces, key=lambda x: -x["size"]):
84    print "Leak of %d bytes at address %s" % (trace["size"], trace["address"])
85    for frame in trace["frames"]:
86      print "  %s (%s)" % (frame["name"], frame["location"])
87    print ""
88
89
90if __name__ == '__main__':
91  Main(sys.argv)
92