1 # Script for converting perf script events into tracing JSON. 2 # 3 # Generated by perf script -g python 4 # Licensed under the terms of the GNU GPL License version 2 5 6 import json 7 import os 8 import sys 9 10 from collections import deque 11 12 13 # Categorize DSOs by component. 14 dso_to_comp = { 15 'libdvm.so': 'Java', 16 'libart.so': 'Java', 17 'libjavacore.so': 'Java', 18 'libandroid_runtime.so': 'Android', 19 'libgui.so': 'Android', 20 'libui.so': 'Android', 21 'libbinder.so': 'Android', 22 'libmemalloc.so': 'Android', 23 'libcrypto.so': 'Android', 24 'libcutils.so':'Android', 25 'libutils.so': 'Android', 26 '[kernel.kallsyms]': 'Kernel', 27 'libc.so': 'Standard Lib', 28 'libstdc++.so': 'Standard Lib', 29 'libm.so':'Standard Lib', 30 'libGLESv2_adreno.so': 'GPU Driver', 31 'libGLESv2_adreno200.so': 'GPU Driver', 32 'libq3dtools_adreno200.so': 'GPU Driver', 33 'libEGL_adreno.so': 'GPU Driver', 34 'libEGL_adreno200.so': 'GPU Driver', 35 'libEGL.so': 'GPU Driver', 36 'libgsl.so': 'GPU Driver', 37 'libGLESv2.so': 'GPU Driver', 38 'libsc-a3xx.so': 'GPU Driver', 39 'libadreno_utils.so': 'GPU Driver', 40 'eglsubAndroid.so': 'GPU Driver', 41 'gralloc.msm8960.so': 'GPU Driver', 42 'libadreno_utils': 'GPU Driver', 43 'libGLES_mali.so': 'GPU Driver', 44 'libchromeview.so': 'Chrome', 45 '[unknown]': '<unknown>', 46 '[UNKNOWN]': '<unknown>', 47 } 48 49 50 def FilterSymbolModule(module): 51 m = dso_to_comp.get(module, None) 52 if m: 53 return m 54 if module.find('libchrome.') == 0: 55 return 'Chrome' 56 if module.find('dalvik') >= 0 or module.find('@') >= 0: 57 return 'Java' 58 return module 59 60 61 def FilterSymbolName(module, orign_module, name): 62 if module == 'Java': 63 return name 64 elif module == 'GPU Driver': 65 return name 66 if name == '': 67 return orign_module + ':unknown' 68 if name[0].isdigit() or name == '(nil)': 69 return orign_module + ':unknown' 70 return name 71 72 73 class StackFrameNode: 74 def __init__(self, stack_id, name, category): 75 self.stack_id = stack_id 76 self.parent_id = 0 77 self.children = {} 78 self.category = category 79 self.name = name 80 self.samples = [] 81 self.total_weight = 0.0 82 self.have_total_weight = False 83 self.parent = None 84 85 def ToDict(self, out_dict): 86 if self.stack_id: 87 node_dict = {} 88 node_dict['name'] = self.name 89 node_dict['category'] = self.category 90 if self.parent_id: 91 node_dict['parent'] = self.parent_id 92 93 out_dict[self.stack_id] = node_dict 94 95 for child in self.children.values(): 96 child.ToDict(out_dict) 97 return out_dict 98 99 def GetTotalWeight(self): 100 if self.have_total_weight: 101 return self.total_weight 102 else: 103 # Sum up self samples weight, and children's total weights. 104 for s in self.samples: 105 self.total_weight += s.weight 106 for c in self.children.values(): 107 self.total_weight += c.GetTotalWeight() 108 self.have_total_weight = True 109 return self.total_weight 110 111 112 class PerfSample: 113 def __init__(self, stack_id, ts, cpu, tid, weight, samp_type, comm): 114 self.stack_id = stack_id 115 self.ts = ts 116 self.cpu = cpu 117 self.tid = tid 118 self.weight = weight 119 self.type = samp_type 120 self.comm = comm 121 122 def ToDict(self): 123 ret = {} 124 ret['ts'] = self.ts / 1000.0 # Timestamp in microseconds 125 ret['tid'] = self.tid # Thread id 126 ret['cpu'] = self.cpu # Sampled CPU 127 ret['weight'] = self.weight # Sample weight 128 ret['name'] = self.type # Sample type 129 ret['comm'] = self.comm # Sample type 130 assert self.stack_id != 0 131 if self.stack_id: 132 ret['sf'] = self.stack_id # Stack frame id 133 return ret 134 135 136 samples = [] 137 root_chain = StackFrameNode(0, 'root', '[unknown]') 138 next_stack_id = 1 139 tot_period = 0 140 saved_period = 0 141 142 143 def process_event(param_dict): 144 global next_stack_id 145 global saved_period 146 global tot_period 147 148 samp_comm = param_dict['comm'] 149 samp_tid = param_dict['tid'] 150 samp_cpu = param_dict['cpu'] 151 samp_ts = param_dict['time'] 152 samp_period = param_dict['period'] 153 samp_type = param_dict['ev_name'] 154 tot_period += samp_period 155 156 # Parse call chain. 157 seen_syms = set() 158 chain = deque() 159 for cs in param_dict['cs']: 160 cs_name = cs[0] 161 cs_dso = os.path.basename(cs[1]) 162 cs_category = FilterSymbolModule(cs_dso) 163 cs_name = FilterSymbolName(cs_category, cs_dso, cs_name) 164 165 if cs_category != '<unknown>' or len(chain) == 0: 166 sym = (cs_name, cs_category) 167 if sym in seen_syms: 168 while chain[0] != sym: 169 seen_syms.remove(chain[0]) 170 chain.popleft() 171 else: 172 seen_syms.add(sym) 173 chain.appendleft(sym) 174 175 # Discard garbage stacktrace before __pthread_start() 176 if cs_name == '__pthread_start(void*)': 177 break 178 179 # Done reading call chain. Add to stack frame tree. 180 stack_frame = root_chain 181 for call in chain: 182 if call in stack_frame.children: 183 stack_frame = stack_frame.children[call] 184 else: 185 new_node = StackFrameNode(next_stack_id, call[0], call[1]) 186 next_stack_id += 1 187 new_node.parent_id = stack_frame.stack_id 188 stack_frame.children[call] = new_node 189 stack_frame = new_node 190 191 # Save sample. 192 sample = PerfSample(stack_frame.stack_id, 193 samp_ts, 194 samp_cpu, 195 samp_tid, 196 samp_period, 197 samp_type, 198 samp_comm) 199 samples.append(sample) 200 stack_frame.samples.append(sample) 201 saved_period += samp_period 202 203 204 def trace_begin(): 205 pass 206 207 208 def trace_end(): 209 # Return siblings of a call tree node. 210 def GetNodeSiblings(node): 211 if not node: 212 return [] 213 if not node.parent: 214 return [] 215 return node.parent.children.values() 216 217 # Try to reduce misplaced stack leaves by moving them up into sibling nodes. 218 def FixCallTree(node, parent): 219 # Get siblings of node's parent. 220 node.parent = parent 221 parent_siblings = GetNodeSiblings(parent) 222 223 # If parent's sibling has same node name, has no children and small weight, 224 # transplant sibling's samples into the current node. 225 for sibling in parent_siblings: 226 if sibling.name == node.name and \ 227 len(sibling.children) == 0 and \ 228 sibling.GetTotalWeight() <= node.GetTotalWeight() * 0.15: 229 230 # Transplant samples from sibling to current node. 231 for samp in sibling.samples: 232 samp.stack_id = node.stack_id 233 node.samples.append(samp) 234 sibling.samples = [] 235 break 236 237 # Recurse child nodes. 238 for c in node.children.values(): 239 FixCallTree(c, node) 240 241 FixCallTree(root_chain, None) 242 243 trace_dict = {} 244 trace_dict['samples'] = [s.ToDict() for s in samples] 245 trace_dict['stackFrames'] = root_chain.ToDict({}) 246 trace_dict['traceEvents'] = [] 247 248 json.dump(trace_dict, sys.stdout, indent=1) 249