| OLD | NEW |
| (Empty) | |
| 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) |
| OLD | NEW |