blob: 280937a8f49b85322138931ad035bb7aebf44fea [file] [log] [blame]
# 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>',
'[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 != '<unknown>' 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)