# Script for converting perf script events into tracing JSON. # # Generated by perf script -g python # Licensed under the terms of the GNU GPL License version 2 import json import os import sys from collections import deque # Categorize DSOs by component. dso_to_comp = { 'libdvm.so': 'Java', 'libart.so': 'Java', 'libjavacore.so': 'Java', 'libandroid_runtime.so': 'Android', 'libgui.so': 'Android', 'libui.so': 'Android', 'libbinder.so': 'Android', 'libmemalloc.so': 'Android', 'libcrypto.so': 'Android', 'libcutils.so':'Android', 'libutils.so': 'Android', '[kernel.kallsyms]': 'Kernel', 'libc.so': 'Standard Lib', 'libstdc++.so': 'Standard Lib', 'libm.so':'Standard Lib', 'libGLESv2_adreno.so': 'GPU Driver', 'libGLESv2_adreno200.so': 'GPU Driver', 'libq3dtools_adreno200.so': 'GPU Driver', 'libEGL_adreno.so': 'GPU Driver', 'libEGL_adreno200.so': 'GPU Driver', 'libEGL.so': 'GPU Driver', 'libgsl.so': 'GPU Driver', 'libGLESv2.so': 'GPU Driver', 'libsc-a3xx.so': 'GPU Driver', 'libadreno_utils.so': 'GPU Driver', 'eglsubAndroid.so': 'GPU Driver', 'gralloc.msm8960.so': 'GPU Driver', 'libadreno_utils': 'GPU Driver', 'libGLES_mali.so': 'GPU Driver', 'libchromeview.so': 'Chrome', '[unknown]': '', '[UNKNOWN]': '', } def FilterSymbolModule(module): m = dso_to_comp.get(module, None) if m: return m if module.find('libchrome.') == 0: return 'Chrome' if module.find('dalvik') >= 0 or module.find('@') >= 0: return 'Java' return module def FilterSymbolName(module, orign_module, name): if module == 'Java': return name elif module == 'GPU Driver': return name if name == '': return orign_module + ':unknown' if name[0].isdigit() or name == '(nil)': return orign_module + ':unknown' return name class StackFrameNode: def __init__(self, stack_id, name, category): self.stack_id = stack_id self.parent_id = 0 self.children = {} self.category = category self.name = name self.samples = [] self.total_weight = 0.0 self.have_total_weight = False self.parent = None def ToDict(self, out_dict): if self.stack_id: node_dict = {} node_dict['name'] = self.name node_dict['category'] = self.category if self.parent_id: node_dict['parent'] = self.parent_id out_dict[self.stack_id] = node_dict for child in self.children.values(): child.ToDict(out_dict) return out_dict def GetTotalWeight(self): if self.have_total_weight: return self.total_weight else: # Sum up self samples weight, and children's total weights. for s in self.samples: self.total_weight += s.weight for c in self.children.values(): self.total_weight += c.GetTotalWeight() self.have_total_weight = True return self.total_weight class PerfSample: def __init__(self, stack_id, ts, cpu, tid, weight, samp_type, comm): self.stack_id = stack_id self.ts = ts self.cpu = cpu self.tid = tid self.weight = weight self.type = samp_type self.comm = comm def ToDict(self): ret = {} ret['ts'] = self.ts / 1000.0 # Timestamp in microseconds ret['tid'] = self.tid # Thread id ret['cpu'] = self.cpu # Sampled CPU ret['weight'] = self.weight # Sample weight ret['name'] = self.type # Sample type ret['comm'] = self.comm # Sample type assert self.stack_id != 0 if self.stack_id: ret['sf'] = self.stack_id # Stack frame id return ret samples = [] root_chain = StackFrameNode(0, 'root', '[unknown]') next_stack_id = 1 tot_period = 0 saved_period = 0 def process_event(param_dict): global next_stack_id global saved_period global tot_period samp_comm = param_dict['comm'] samp_tid = param_dict['tid'] samp_cpu = param_dict['cpu'] samp_ts = param_dict['time'] samp_period = param_dict['period'] samp_type = param_dict['ev_name'] tot_period += samp_period # Parse call chain. seen_syms = set() chain = deque() for cs in param_dict['cs']: cs_name = cs[0] cs_dso = os.path.basename(cs[1]) cs_category = FilterSymbolModule(cs_dso) cs_name = FilterSymbolName(cs_category, cs_dso, cs_name) if cs_category != '' or len(chain) == 0: sym = (cs_name, cs_category) if sym in seen_syms: while chain[0] != sym: seen_syms.remove(chain[0]) chain.popleft() else: seen_syms.add(sym) chain.appendleft(sym) # Discard garbage stacktrace before __pthread_start() if cs_name == '__pthread_start(void*)': break # Done reading call chain. Add to stack frame tree. stack_frame = root_chain for call in chain: if call in stack_frame.children: stack_frame = stack_frame.children[call] else: new_node = StackFrameNode(next_stack_id, call[0], call[1]) next_stack_id += 1 new_node.parent_id = stack_frame.stack_id stack_frame.children[call] = new_node stack_frame = new_node # Save sample. sample = PerfSample(stack_frame.stack_id, samp_ts, samp_cpu, samp_tid, samp_period, samp_type, samp_comm) samples.append(sample) stack_frame.samples.append(sample) saved_period += samp_period def trace_begin(): pass def trace_end(): # Return siblings of a call tree node. def GetNodeSiblings(node): if not node: return [] if not node.parent: return [] return node.parent.children.values() # Try to reduce misplaced stack leaves by moving them up into sibling nodes. def FixCallTree(node, parent): # Get siblings of node's parent. node.parent = parent parent_siblings = GetNodeSiblings(parent) # If parent's sibling has same node name, has no children and small weight, # transplant sibling's samples into the current node. for sibling in parent_siblings: if sibling.name == node.name and \ len(sibling.children) == 0 and \ sibling.GetTotalWeight() <= node.GetTotalWeight() * 0.15: # Transplant samples from sibling to current node. for samp in sibling.samples: samp.stack_id = node.stack_id node.samples.append(samp) sibling.samples = [] break # Recurse child nodes. for c in node.children.values(): FixCallTree(c, node) FixCallTree(root_chain, None) trace_dict = {} trace_dict['samples'] = [s.ToDict() for s in samples] trace_dict['stackFrames'] = root_chain.ToDict({}) trace_dict['traceEvents'] = [] json.dump(trace_dict, sys.stdout, indent=1)