OLD | NEW |
| (Empty) |
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) | |
OLD | NEW |