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