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