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