Home | History | Annotate | Download | only in subcommands
      1 # Copyright 2013 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 import sys
      7 
      8 from lib.policy import PolicySet
      9 from lib.subcommand import SubCommand
     10 
     11 
     12 LOGGER = logging.getLogger('dmprof')
     13 
     14 
     15 class PProfCommand(SubCommand):
     16   def __init__(self):
     17     super(PProfCommand, self).__init__(
     18         'Usage: %prog pprof [-c COMPONENT] <dump> <policy>')
     19     self._parser.add_option('-c', '--component', type='string',
     20                             dest='component',
     21                             help='restrict to COMPONENT', metavar='COMPONENT')
     22 
     23   def do(self, sys_argv):
     24     options, args = self._parse_args(sys_argv, 2)
     25 
     26     dump_path = args[1]
     27     target_policy = args[2]
     28     component = options.component
     29 
     30     (bucket_set, dump) = SubCommand.load_basic_files(dump_path, False)
     31     policy_set = PolicySet.load(SubCommand._parse_policy_list(target_policy))
     32 
     33     with open(SubCommand._find_prefix(dump_path) + '.maps', 'r') as maps_f:
     34       maps_lines = maps_f.readlines()
     35     PProfCommand._output(
     36         dump, policy_set[target_policy], bucket_set, maps_lines, component,
     37         sys.stdout)
     38 
     39     return 0
     40 
     41   @staticmethod
     42   def _output(dump, policy, bucket_set, maps_lines, component_name, out):
     43     """Converts the heap profile dump so it can be processed by pprof.
     44 
     45     Args:
     46         dump: A Dump object.
     47         policy: A Policy object.
     48         bucket_set: A BucketSet object.
     49         maps_lines: A list of strings containing /proc/.../maps.
     50         component_name: A name of component for filtering.
     51         out: An IO object to output.
     52     """
     53     out.write('heap profile: ')
     54     com_committed, com_allocs = PProfCommand._accumulate(
     55         dump, policy, bucket_set, component_name)
     56 
     57     out.write('%6d: %8s [%6d: %8s] @ heapprofile\n' % (
     58         com_allocs, com_committed, com_allocs, com_committed))
     59 
     60     PProfCommand._output_stacktrace_lines(
     61         dump, policy, bucket_set, component_name, out)
     62 
     63     out.write('MAPPED_LIBRARIES:\n')
     64     for line in maps_lines:
     65       out.write(line)
     66 
     67   @staticmethod
     68   def _accumulate(dump, policy, bucket_set, component_name):
     69     """Accumulates size of committed chunks and the number of allocated chunks.
     70 
     71     Args:
     72         dump: A Dump object.
     73         policy: A Policy object.
     74         bucket_set: A BucketSet object.
     75         component_name: A name of component for filtering.
     76 
     77     Returns:
     78         Two integers which are the accumulated size of committed regions and the
     79         number of allocated chunks, respectively.
     80     """
     81     com_committed = 0
     82     com_allocs = 0
     83 
     84     for _, region in dump.iter_map:
     85       if region[0] != 'hooked':
     86         continue
     87       component_match, bucket = policy.find_mmap(region, bucket_set)
     88 
     89       if (component_name and component_name != component_match) or (
     90           region[1]['committed'] == 0):
     91         continue
     92 
     93       com_committed += region[1]['committed']
     94       com_allocs += 1
     95 
     96     for bucket_id, _, committed, allocs, frees in dump.iter_stacktrace:
     97       bucket = bucket_set.get(bucket_id)
     98       if not bucket or bucket.allocator_type == 'malloc':
     99         component_match = policy.find_malloc(bucket)
    100       elif bucket.allocator_type == 'mmap':
    101         continue
    102       else:
    103         assert False
    104       if (not bucket or
    105           (component_name and component_name != component_match)):
    106         continue
    107 
    108       com_committed += committed
    109       com_allocs += allocs - frees
    110 
    111     return com_committed, com_allocs
    112 
    113   @staticmethod
    114   def _output_stacktrace_lines(dump, policy, bucket_set, component_name, out):
    115     """Prints information of stacktrace lines for pprof.
    116 
    117     Args:
    118         dump: A Dump object.
    119         policy: A Policy object.
    120         bucket_set: A BucketSet object.
    121         component_name: A name of component for filtering.
    122         out: An IO object to output.
    123     """
    124     for _, region in dump.iter_map:
    125       if region[0] != 'hooked':
    126         continue
    127       component_match, bucket = policy.find_mmap(region, bucket_set)
    128 
    129       if (component_name and component_name != component_match) or (
    130           region[1]['committed'] == 0):
    131         continue
    132 
    133       out.write('     1: %8s [     1: %8s] @' % (
    134           region[1]['committed'], region[1]['committed']))
    135       for address in bucket.stacktrace:
    136         out.write(' 0x%016x' % address)
    137       out.write('\n')
    138 
    139     for bucket_id, _, committed, allocs, frees in dump.iter_stacktrace:
    140       bucket = bucket_set.get(bucket_id)
    141       if not bucket or bucket.allocator_type == 'malloc':
    142         component_match = policy.find_malloc(bucket)
    143       elif bucket.allocator_type == 'mmap':
    144         continue
    145       else:
    146         assert False
    147       if (not bucket or
    148           (component_name and component_name != component_match)):
    149         continue
    150 
    151       out.write('%6d: %8s [%6d: %8s] @' % (
    152           allocs - frees, str(committed), allocs - frees, str(committed)))
    153       for address in bucket.stacktrace:
    154         out.write(' 0x%016x' % address)
    155       out.write('\n')
    156