1#!/usr/bin/python 2# Copyright (C) 2018 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import argparse 17 18from google.protobuf import descriptor, descriptor_pb2, message_factory, reflection 19from google.protobuf.pyext import _message 20 21CLONE_THREAD = 0x00010000 22CLONE_VFORK = 0x00004000 23CLONE_VM = 0x00000100 24 25 26class Trace(object): 27 28 def __init__(self, trace): 29 self.trace = trace 30 self.proc_map = {} 31 self.proc_map[0] = 'idle_thread' 32 33 def add_system_info(self, arch="", fingerprint=""): 34 self.packet = self.trace.packet.add() 35 self.packet.system_info.utsname.machine = arch 36 self.packet.system_info.android_build_fingerprint = fingerprint 37 38 def add_ftrace_packet(self, cpu): 39 self.packet = self.trace.packet.add() 40 self.packet.ftrace_events.cpu = cpu 41 42 def add_packet(self, ts=None): 43 self.packet = self.trace.packet.add() 44 if ts is not None: 45 self.packet.timestamp = ts 46 return self.packet 47 48 def __add_ftrace_event(self, ts, tid): 49 ftrace = self.packet.ftrace_events.event.add() 50 ftrace.timestamp = ts 51 ftrace.pid = tid 52 return ftrace 53 54 def add_rss_stat(self, ts, tid, member, size, mm_id=None, curr=None): 55 ftrace = self.__add_ftrace_event(ts, tid) 56 rss_stat = ftrace.rss_stat 57 rss_stat.member = member 58 rss_stat.size = size 59 if mm_id is not None: 60 rss_stat.mm_id = mm_id 61 if curr is not None: 62 rss_stat.curr = curr 63 64 def add_ion_event(self, ts, tid, heap_name, len, size=0): 65 ftrace = self.__add_ftrace_event(ts, tid) 66 ion = ftrace.ion_heap_grow 67 ion.heap_name = heap_name 68 ion.len = len 69 ion.total_allocated = size 70 71 def add_oom_score_update(self, ts, oom_score_adj, pid): 72 ftrace = self.__add_ftrace_event(ts, pid) 73 oom_score = ftrace.oom_score_adj_update 74 oom_score.comm = self.proc_map[pid] 75 oom_score.oom_score_adj = oom_score_adj 76 oom_score.pid = pid 77 78 def add_sched(self, 79 ts, 80 prev_pid, 81 next_pid, 82 prev_comm=None, 83 next_comm=None, 84 prev_state=None): 85 ftrace = self.__add_ftrace_event(ts, 0) 86 ss = ftrace.sched_switch 87 ss.prev_comm = prev_comm or self.proc_map[prev_pid] 88 ss.prev_pid = prev_pid 89 ss.next_pid = next_pid 90 ss.next_comm = next_comm or self.proc_map[next_pid] 91 if prev_state: 92 if prev_state == 'R': 93 ss.prev_state = 0 94 elif prev_state == 'S': 95 ss.prev_state = 1 96 elif prev_state == 'U': 97 ss.prev_state = 2 98 else: 99 raise Exception('Invalid prev state {}'.format(prev_state)) 100 101 def add_cpufreq(self, ts, freq, cpu): 102 ftrace = self.__add_ftrace_event(ts, 0) 103 cpufreq = ftrace.cpu_frequency 104 cpufreq.state = freq 105 cpufreq.cpu_id = cpu 106 107 def add_kernel_lmk(self, ts, tid): 108 ftrace = self.__add_ftrace_event(ts, tid) 109 lowmemory_kill = ftrace.lowmemory_kill 110 lowmemory_kill.pid = tid 111 112 def add_sys_enter(self, ts, tid, id): 113 ftrace = self.__add_ftrace_event(ts, tid) 114 sys_enter = ftrace.sys_enter 115 sys_enter.id = id 116 117 def add_sys_exit(self, ts, tid, id, ret): 118 ftrace = self.__add_ftrace_event(ts, tid) 119 sys_exit = ftrace.sys_exit 120 sys_exit.id = id 121 sys_exit.ret = ret 122 123 def add_newtask(self, ts, tid, new_tid, new_comm, flags): 124 ftrace = self.__add_ftrace_event(ts, tid) 125 newtask = ftrace.task_newtask 126 newtask.pid = new_tid 127 newtask.comm = new_comm 128 newtask.clone_flags = flags 129 130 def add_process_free(self, ts, tid, comm, prio): 131 ftrace = self.__add_ftrace_event(ts, tid) 132 sched_process_free = ftrace.sched_process_free 133 sched_process_free.pid = tid 134 sched_process_free.comm = comm 135 sched_process_free.prio = prio 136 137 def add_rename(self, ts, tid, old_comm, new_comm, oom_score_adj): 138 ftrace = self.__add_ftrace_event(ts, tid) 139 task_rename = ftrace.task_rename 140 task_rename.pid = tid 141 task_rename.oldcomm = old_comm 142 task_rename.newcomm = new_comm 143 task_rename.oom_score_adj = oom_score_adj 144 145 def add_print(self, ts, tid, buf): 146 ftrace = self.__add_ftrace_event(ts, tid) 147 print_event = getattr(ftrace, 'print') 148 print_event.buf = buf 149 150 def add_kmalloc(self, ts, tid, bytes_alloc, bytes_req, call_site, gfp_flags, 151 ptr): 152 ftrace = self.__add_ftrace_event(ts, tid) 153 kmalloc = ftrace.kmalloc 154 kmalloc.bytes_alloc = bytes_alloc 155 kmalloc.bytes_req = bytes_req 156 kmalloc.call_site = call_site 157 kmalloc.gfp_flags = gfp_flags 158 kmalloc.ptr = ptr 159 160 def add_kfree(self, ts, tid, call_site, ptr): 161 ftrace = self.__add_ftrace_event(ts, tid) 162 kfree = ftrace.kfree 163 kfree.call_site = call_site 164 kfree.ptr = ptr 165 166 def add_atrace_counter(self, ts, pid, tid, buf, cnt): 167 self.add_print(ts, tid, 'C|{}|{}|{}'.format(pid, buf, cnt)) 168 169 def add_atrace_begin(self, ts, tid, pid, buf): 170 self.add_print(ts, tid, 'B|{}|{}'.format(pid, buf)) 171 172 def add_atrace_end(self, ts, tid, pid): 173 self.add_print(ts, tid, 'E|{}'.format(pid)) 174 175 def add_atrace_async_begin(self, ts, tid, pid, buf): 176 self.add_print(ts, tid, 'S|{}|{}|0'.format(pid, buf)) 177 178 def add_atrace_async_end(self, ts, tid, pid, buf): 179 self.add_print(ts, tid, 'F|{}|{}|0'.format(pid, buf)) 180 181 def add_process(self, pid, ppid, cmdline, uid=None): 182 process = self.packet.process_tree.processes.add() 183 process.pid = pid 184 process.ppid = ppid 185 process.cmdline.append(cmdline) 186 if uid is not None: 187 process.uid = uid 188 self.proc_map[pid] = cmdline 189 190 def add_thread(self, tid, tgid, cmdline): 191 thread = self.packet.process_tree.threads.add() 192 thread.tid = tid 193 thread.tgid = tgid 194 self.proc_map[tid] = cmdline 195 196 def add_battery_counters(self, ts, charge_uah, cap_prct, curr_ua, 197 curr_avg_ua): 198 self.packet = self.trace.packet.add() 199 self.packet.timestamp = ts 200 battery_count = self.packet.battery 201 battery_count.charge_counter_uah = charge_uah 202 battery_count.capacity_percent = cap_prct 203 battery_count.current_ua = curr_ua 204 battery_count.current_avg_ua = curr_avg_ua 205 206 def add_battery_counters_no_curr_ua(self, ts, charge_uah, cap_prct, 207 curr_avg_ua): 208 self.packet = self.trace.packet.add() 209 self.packet.timestamp = ts 210 battery_count = self.packet.battery 211 battery_count.charge_counter_uah = charge_uah 212 battery_count.capacity_percent = cap_prct 213 battery_count.current_avg_ua = curr_avg_ua 214 215 def add_power_rails_desc(self, index_val, name): 216 power_rails = self.packet.power_rails 217 descriptor = power_rails.rail_descriptor.add() 218 descriptor.index = index_val 219 descriptor.rail_name = name 220 221 def add_power_rails_data(self, ts, index_val, value): 222 power_rails = self.packet.power_rails 223 energy_data = power_rails.energy_data.add() 224 energy_data.index = index_val 225 energy_data.timestamp_ms = ts 226 energy_data.energy = value 227 228 def add_package_list(self, ts, name, uid, version_code): 229 packet = self.add_packet() 230 packet.timestamp = ts 231 plist = packet.packages_list 232 pinfo = plist.packages.add() 233 pinfo.name = name 234 pinfo.uid = uid 235 pinfo.version_code = version_code 236 237 def add_profile_packet(self, ts): 238 packet = self.add_packet() 239 packet.timestamp = ts 240 return packet.profile_packet 241 242 def add_clock_snapshot(self, clocks, seq_id=None): 243 packet = self.add_packet() 244 if seq_id is not None: 245 packet.trusted_packet_sequence_id = seq_id 246 snap = self.packet.clock_snapshot 247 for k, v in clocks.iteritems(): 248 clock = snap.clocks.add() 249 clock.clock_id = k 250 clock.timestamp = v 251 252 def add_gpu_counter_spec(self, 253 ts, 254 counter_id, 255 name, 256 description=None, 257 unit_numerators=[], 258 unit_denominators=[]): 259 packet = self.add_packet() 260 packet.timestamp = ts 261 gpu_counters = packet.gpu_counter_event 262 counter_desc = gpu_counters.counter_descriptor 263 spec = counter_desc.specs.add() 264 spec.counter_id = counter_id 265 spec.name = name 266 if description is not None: 267 spec.description = description 268 spec.numerator_units.extend(unit_numerators) 269 spec.denominator_units.extend(unit_denominators) 270 271 def add_gpu_counter(self, ts, counter_id, value, clock_id=None, seq_id=None): 272 packet = self.add_packet() 273 packet.timestamp = ts 274 if clock_id is not None: 275 packet.timestamp_clock_id = clock_id 276 if seq_id is not None: 277 packet.trusted_packet_sequence_id = seq_id 278 gpu_counters = self.packet.gpu_counter_event 279 gpu_counter = gpu_counters.counters.add() 280 gpu_counter.counter_id = counter_id 281 gpu_counter.int_value = value 282 283 def add_gpu_render_stages_hw_queue_spec(self, specs=[]): 284 packet = self.add_packet() 285 spec = self.packet.gpu_render_stage_event.specifications 286 for s in specs: 287 hw_queue = spec.hw_queue.add() 288 hw_queue.name = s.get('name', '') 289 if 'description' in s: 290 hw_queue.description = s['description'] 291 292 def add_gpu_render_stages_stage_spec(self, specs=[]): 293 packet = self.add_packet() 294 spec = self.packet.gpu_render_stage_event.specifications 295 for s in specs: 296 stage = spec.stage.add() 297 stage.name = s.get('name', '') 298 if 'description' in s: 299 stage.description = s['description'] 300 301 def add_gpu_render_stages(self, 302 ts, 303 event_id, 304 duration, 305 hw_queue_id, 306 stage_id, 307 context, 308 render_target_handle=None, 309 render_pass_handle=None, 310 command_buffer_handle=None, 311 submission_id=None, 312 extra_data={}): 313 packet = self.add_packet() 314 packet.timestamp = ts 315 render_stage = self.packet.gpu_render_stage_event 316 render_stage.event_id = event_id 317 render_stage.duration = duration 318 render_stage.hw_queue_id = hw_queue_id 319 render_stage.stage_id = stage_id 320 render_stage.context = context 321 if render_target_handle is not None: 322 render_stage.render_target_handle = render_target_handle 323 if render_pass_handle is not None: 324 render_stage.render_pass_handle = render_pass_handle 325 if command_buffer_handle is not None: 326 render_stage.command_buffer_handle = command_buffer_handle 327 if submission_id is not None: 328 render_stage.submission_id = submission_id 329 for key, value in extra_data.items(): 330 data = render_stage.extra_data.add() 331 data.name = key 332 if value is not None: 333 data.value = value 334 335 def add_vk_debug_marker(self, ts, pid, vk_device, obj_type, obj, obj_name): 336 packet = self.add_packet() 337 packet.timestamp = ts 338 debug_marker = (self.packet.vulkan_api_event.vk_debug_utils_object_name) 339 debug_marker.pid = pid 340 debug_marker.vk_device = vk_device 341 debug_marker.object_type = obj_type 342 debug_marker.object = obj 343 debug_marker.object_name = obj_name 344 345 def add_vk_queue_submit(self, ts, dur, pid, tid, vk_queue, vk_command_buffers, 346 submission_id): 347 packet = self.add_packet() 348 packet.timestamp = ts 349 submit = (self.packet.vulkan_api_event.vk_queue_submit) 350 submit.duration_ns = dur 351 submit.pid = pid 352 submit.tid = tid 353 for cmd in vk_command_buffers: 354 submit.vk_command_buffers.append(cmd) 355 submit.submission_id = submission_id 356 357 def add_gpu_log(self, ts, severity, tag, message): 358 packet = self.add_packet() 359 packet.timestamp = ts 360 gpu_log = self.packet.gpu_log 361 gpu_log.severity = severity 362 gpu_log.tag = tag 363 gpu_log.log_message = message 364 365 def add_buffer_event_packet(self, ts, buffer_id, layer_name, frame_number, 366 event_type, duration): 367 packet = self.add_packet() 368 packet.timestamp = ts 369 buffer_event = packet.graphics_frame_event.buffer_event 370 if buffer_id >= 0: 371 buffer_event.buffer_id = buffer_id 372 buffer_event.layer_name = layer_name 373 buffer_event.frame_number = frame_number 374 if event_type >= 0: 375 buffer_event.type = event_type 376 buffer_event.duration_ns = duration 377 378 def add_cpu(self, freqs): 379 cpu = self.packet.cpu_info.cpus.add() 380 for freq in freqs: 381 cpu.frequencies.append(freq) 382 383 def add_process_stats(self, pid, freqs): 384 process = self.packet.process_stats.processes.add() 385 process.pid = pid 386 thread = process.threads.add() 387 thread.tid = pid * 10 388 for index in freqs: 389 thread.cpu_freq_indices.append(index) 390 thread.cpu_freq_ticks.append(freqs[index]) 391 392 393def create_trace(): 394 parser = argparse.ArgumentParser() 395 parser.add_argument( 396 'trace_descriptor', type=str, help='location of trace descriptor') 397 args = parser.parse_args() 398 399 with open(args.trace_descriptor, 'rb') as t: 400 fileContent = t.read() 401 402 file_desc_set_pb2 = descriptor_pb2.FileDescriptorSet() 403 file_desc_set_pb2.MergeFromString(fileContent) 404 405 desc_by_path = {} 406 for f_desc_pb2 in file_desc_set_pb2.file: 407 f_desc_pb2_encode = f_desc_pb2.SerializeToString() 408 f_desc = descriptor.FileDescriptor( 409 name=f_desc_pb2.name, 410 package=f_desc_pb2.package, 411 serialized_pb=f_desc_pb2_encode) 412 413 for desc in f_desc.message_types_by_name.values(): 414 desc_by_path[desc.full_name] = desc 415 416 trace = message_factory.MessageFactory().GetPrototype( 417 desc_by_path['perfetto.protos.Trace'])() 418 return Trace(trace) 419