Chromium Code Reviews| Index: tools/android/adb_profile_chrome/perf_to_tracing.py |
| diff --git a/tools/android/adb_profile_chrome/perf_to_tracing.py b/tools/android/adb_profile_chrome/perf_to_tracing.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..36bf058f41e0688e1aaf3a956779bbbd54420109 |
| --- /dev/null |
| +++ b/tools/android/adb_profile_chrome/perf_to_tracing.py |
| @@ -0,0 +1,249 @@ |
| +# 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 |
|
vmiura
2014/07/17 22:51:17
Should we replace this with Chrome's license?
Sami
2014/07/18 11:19:12
I'm not sure, but I think we need to keep the lice
|
| + |
| +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 #orign_module |
|
vmiura
2014/07/17 22:51:17
nit: remove comment
Sami
2014/07/18 11:19:13
Done.
|
| + elif module == 'GPU Driver': |
| + return name # orign_module |
|
vmiura
2014/07/17 22:51:17
nit: remove comment
Sami
2014/07/18 11:19:13
Done.
|
| + 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_pid = param_dict['pid'] |
|
vmiura
2014/07/17 22:51:17
nit: Remove commented line
Sami
2014/07/18 11:19:13
Done.
|
| + 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 mobing them up into sibling nodes. |
|
vmiura
2014/07/17 22:51:17
nit: mobing -> moving
Sami
2014/07/18 11:19:13
Done.
|
| + 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) |