Home | History | Annotate | Download | only in third_party
      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