Home | History | Annotate | Download | only in tcmalloc
      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
      7 HeapProfilerDumpLiveObjects.
      8 """
      9 
     10 import os
     11 import re
     12 import subprocess
     13 import sys
     14 import tempfile
     15 
     16 def usage():
     17   print """\
     18 Usage:
     19   tools/tcmalloc/print-live-objects.py out/Debug/chrome leaks.dmp
     20 """
     21 
     22 def 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 
     41 def 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 
     68 def 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 
     90 if __name__ == '__main__':
     91   Main(sys.argv)
     92