| Index: tools/profile_chrome/third_party/perf_to_tracing.py
|
| diff --git a/tools/profile_chrome/third_party/perf_to_tracing.py b/tools/profile_chrome/third_party/perf_to_tracing.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..280937a8f49b85322138931ad035bb7aebf44fea
|
| --- /dev/null
|
| +++ b/tools/profile_chrome/third_party/perf_to_tracing.py
|
| @@ -0,0 +1,248 @@
|
| +# 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)
|
|
|