| #!/usr/bin/env python3 | |
| # Copyright (C) 2018 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import argparse | |
| from collections import namedtuple | |
| from google.protobuf import descriptor_pb2, message_factory, descriptor_pool | |
| CLONE_THREAD = 0x00010000 | |
| CLONE_VFORK = 0x00004000 | |
| CLONE_VM = 0x00000100 | |
| # For compatibility with older pb module versions. | |
| def get_message_class(pool, msg): | |
| if hasattr(message_factory, "GetMessageClass"): | |
| return message_factory.GetMessageClass(msg) | |
| else: | |
| return message_factory.MessageFactory(pool).GetPrototype(msg) | |
| def ms_to_ns(time_in_ms): | |
| return int(time_in_ms * 1000000) | |
| def s_to_ns(time_in_s): | |
| return int(time_in_s * 1000000000) | |
| class Trace(object): | |
| def __init__(self, trace, prototypes): | |
| self.trace = trace | |
| self.prototypes = prototypes | |
| self.proc_map = {} | |
| self.proc_map[0] = 'idle_thread' | |
| def add_system_info(self, arch="", fingerprint=""): | |
| self.packet = self.trace.packet.add() | |
| self.packet.system_info.utsname.machine = arch | |
| self.packet.system_info.android_build_fingerprint = fingerprint | |
| def add_ftrace_packet(self, cpu): | |
| self.packet = self.trace.packet.add() | |
| self.packet.ftrace_events.cpu = cpu | |
| def add_packet(self, ts=None): | |
| self.packet = self.trace.packet.add() | |
| if ts is not None: | |
| self.packet.timestamp = ts | |
| return self.packet | |
| def __add_ftrace_event(self, ts, tid): | |
| ftrace = self.packet.ftrace_events.event.add() | |
| ftrace.timestamp = ts | |
| ftrace.pid = tid | |
| return ftrace | |
| def add_rss_stat(self, ts, tid, member, size, mm_id=None, curr=None): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| rss_stat = ftrace.rss_stat | |
| rss_stat.member = member | |
| rss_stat.size = size | |
| if mm_id is not None: | |
| rss_stat.mm_id = mm_id | |
| if curr is not None: | |
| rss_stat.curr = curr | |
| def add_ion_event(self, ts, tid, heap_name, len, size=0): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| ion = ftrace.ion_heap_grow | |
| ion.heap_name = heap_name | |
| ion.len = len | |
| ion.total_allocated = size | |
| def add_oom_score_update(self, ts, oom_score_adj, pid): | |
| ftrace = self.__add_ftrace_event(ts, pid) | |
| oom_score = ftrace.oom_score_adj_update | |
| oom_score.comm = self.proc_map[pid] | |
| oom_score.oom_score_adj = oom_score_adj | |
| oom_score.pid = pid | |
| def add_sched(self, | |
| ts, | |
| prev_pid, | |
| next_pid, | |
| prev_comm=None, | |
| next_comm=None, | |
| prev_state=None): | |
| ftrace = self.__add_ftrace_event(ts, 0) | |
| ss = ftrace.sched_switch | |
| ss.prev_comm = prev_comm or self.proc_map[prev_pid] | |
| ss.prev_pid = prev_pid | |
| ss.next_pid = next_pid | |
| ss.next_comm = next_comm or self.proc_map[next_pid] | |
| if prev_state: | |
| if prev_state == 'R': | |
| ss.prev_state = 0 | |
| elif prev_state == 'S': | |
| ss.prev_state = 1 | |
| elif prev_state == 'U' or prev_state == 'D': | |
| ss.prev_state = 2 | |
| else: | |
| raise Exception('Invalid prev state {}'.format(prev_state)) | |
| def add_cpufreq(self, ts, freq, cpu): | |
| ftrace = self.__add_ftrace_event(ts, 0) | |
| cpufreq = ftrace.cpu_frequency | |
| cpufreq.state = freq | |
| cpufreq.cpu_id = cpu | |
| def add_kernel_lmk(self, ts, tid): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| lowmemory_kill = ftrace.lowmemory_kill | |
| lowmemory_kill.pid = tid | |
| def add_sys_enter(self, ts, tid, id): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| sys_enter = ftrace.sys_enter | |
| sys_enter.id = id | |
| def add_sys_exit(self, ts, tid, id, ret): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| sys_exit = ftrace.sys_exit | |
| sys_exit.id = id | |
| sys_exit.ret = ret | |
| def add_newtask(self, ts, tid, new_tid, new_comm, flags): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| newtask = ftrace.task_newtask | |
| newtask.pid = new_tid | |
| newtask.comm = new_comm | |
| newtask.clone_flags = flags | |
| def add_process_free(self, ts, tid, comm, prio): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| sched_process_free = ftrace.sched_process_free | |
| sched_process_free.pid = tid | |
| sched_process_free.comm = comm | |
| sched_process_free.prio = prio | |
| def add_rename(self, ts, tid, old_comm, new_comm, oom_score_adj): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| task_rename = ftrace.task_rename | |
| task_rename.pid = tid | |
| task_rename.oldcomm = old_comm | |
| task_rename.newcomm = new_comm | |
| task_rename.oom_score_adj = oom_score_adj | |
| def add_print(self, ts, tid, buf): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| print_event = getattr(ftrace, 'print') | |
| print_event.buf = buf | |
| def add_kmalloc(self, ts, tid, bytes_alloc, bytes_req, call_site, gfp_flags, | |
| ptr): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| kmalloc = ftrace.kmalloc | |
| kmalloc.bytes_alloc = bytes_alloc | |
| kmalloc.bytes_req = bytes_req | |
| kmalloc.call_site = call_site | |
| kmalloc.gfp_flags = gfp_flags | |
| kmalloc.ptr = ptr | |
| def add_kfree(self, ts, tid, call_site, ptr): | |
| ftrace = self.__add_ftrace_event(ts, tid) | |
| kfree = ftrace.kfree | |
| kfree.call_site = call_site | |
| kfree.ptr = ptr | |
| def add_atrace_counter(self, ts, pid, tid, buf, cnt): | |
| self.add_print(ts, tid, 'C|{}|{}|{}'.format(pid, buf, cnt)) | |
| def add_atrace_begin(self, ts, tid, pid, buf): | |
| self.add_print(ts, tid, 'B|{}|{}'.format(pid, buf)) | |
| def add_atrace_end(self, ts, tid, pid): | |
| self.add_print(ts, tid, 'E|{}'.format(pid)) | |
| def add_atrace_async_begin(self, ts, tid, pid, buf): | |
| self.add_print(ts, tid, 'S|{}|{}|0'.format(pid, buf)) | |
| def add_atrace_async_end(self, ts, tid, pid, buf): | |
| self.add_print(ts, tid, 'F|{}|{}|0'.format(pid, buf)) | |
| def add_atrace_instant(self, ts, tid, pid, buf): | |
| self.add_print(ts, tid, 'I|{}|{}'.format(pid, buf)) | |
| def add_atrace_instant_for_track(self, ts, tid, pid, track_name, buf): | |
| self.add_print(ts, tid, 'N|{}|{}|{}'.format(pid, track_name, buf)) | |
| def add_atrace_for_thread(self, ts, ts_end, buf, tid, pid): | |
| self.add_atrace_begin(ts=ts, tid=tid, pid=pid, buf=buf) | |
| self.add_atrace_end(ts=ts_end, tid=tid, pid=pid) | |
| def add_async_atrace_for_thread(self, ts, ts_end, buf, tid, pid): | |
| self.add_atrace_async_begin(ts=ts, tid=tid, pid=pid, buf=buf) | |
| self.add_atrace_async_end(ts=ts_end, tid=tid, pid=pid, buf=buf) | |
| def add_frame(self, vsync, ts_do_frame, ts_end_do_frame, tid, pid): | |
| self.add_atrace_for_thread( | |
| ts=ts_do_frame, | |
| ts_end=ts_end_do_frame, | |
| buf="Choreographer#doFrame %d" % vsync, | |
| tid=tid, | |
| pid=pid) | |
| def add_process(self, pid, ppid, cmdline, uid=None): | |
| process = self.packet.process_tree.processes.add() | |
| process.pid = pid | |
| process.ppid = ppid | |
| process.cmdline.append(cmdline) | |
| if uid is not None: | |
| process.uid = uid | |
| self.proc_map[pid] = cmdline | |
| def add_thread(self, tid, tgid, cmdline, name=None): | |
| thread = self.packet.process_tree.threads.add() | |
| thread.tid = tid | |
| thread.tgid = tgid | |
| if name is not None: | |
| thread.name = name | |
| self.proc_map[tid] = cmdline | |
| def add_battery_counters(self, ts, charge_uah, cap_prct, curr_ua, curr_avg_ua, | |
| voltage_uv): | |
| self.packet = self.trace.packet.add() | |
| self.packet.timestamp = ts | |
| battery_count = self.packet.battery | |
| battery_count.charge_counter_uah = charge_uah | |
| battery_count.capacity_percent = cap_prct | |
| battery_count.current_ua = curr_ua | |
| battery_count.current_avg_ua = curr_avg_ua | |
| battery_count.voltage_uv = voltage_uv | |
| def add_binder_transaction(self, transaction_id, ts_start, ts_end, tid, pid, | |
| reply_id, reply_ts_start, reply_ts_end, reply_tid, | |
| reply_pid): | |
| # Binder transaction start. | |
| ftrace = self.__add_ftrace_event(ts_start, tid) | |
| binder_transaction = ftrace.binder_transaction | |
| binder_transaction.debug_id = transaction_id | |
| binder_transaction.to_proc = reply_pid | |
| binder_transaction.to_thread = reply_tid | |
| binder_transaction.reply = 0 | |
| # Binder reply start | |
| ftrace = self.__add_ftrace_event(reply_ts_start, reply_tid) | |
| binder_transaction_received = ftrace.binder_transaction_received | |
| binder_transaction_received.debug_id = transaction_id | |
| # Binder reply finish | |
| ftrace = self.__add_ftrace_event(reply_ts_end, reply_tid) | |
| reply_binder_transaction = ftrace.binder_transaction | |
| reply_binder_transaction.debug_id = reply_id | |
| reply_binder_transaction.to_proc = pid | |
| reply_binder_transaction.to_thread = tid | |
| reply_binder_transaction.reply = 1 | |
| # Binder transaction finish | |
| ftrace = self.__add_ftrace_event(ts_end, tid) | |
| reply_binder_transaction_received = ftrace.binder_transaction_received | |
| reply_binder_transaction_received.debug_id = reply_id | |
| def add_battery_counters_no_curr_ua(self, ts, charge_uah, cap_prct, | |
| curr_avg_ua, voltage_uv): | |
| self.packet = self.trace.packet.add() | |
| self.packet.timestamp = ts | |
| battery_count = self.packet.battery | |
| battery_count.charge_counter_uah = charge_uah | |
| battery_count.capacity_percent = cap_prct | |
| battery_count.current_avg_ua = curr_avg_ua | |
| battery_count.voltage_uv = voltage_uv | |
| def add_power_rails_desc(self, index_val, name): | |
| power_rails = self.packet.power_rails | |
| descriptor = power_rails.rail_descriptor.add() | |
| descriptor.index = index_val | |
| descriptor.rail_name = name | |
| def add_power_rails_data(self, ts, index_val, value): | |
| power_rails = self.packet.power_rails | |
| energy_data = power_rails.energy_data.add() | |
| energy_data.index = index_val | |
| energy_data.timestamp_ms = ts | |
| energy_data.energy = value | |
| def add_package_list(self, ts, name, uid, version_code): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| plist = packet.packages_list | |
| pinfo = plist.packages.add() | |
| pinfo.name = name | |
| pinfo.uid = uid | |
| pinfo.version_code = version_code | |
| def add_debuggable_package_list(self, ts, name, uid, version_code): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| plist = packet.packages_list | |
| pinfo = plist.packages.add() | |
| pinfo.name = name | |
| pinfo.uid = uid | |
| pinfo.version_code = version_code | |
| pinfo.debuggable = True | |
| def add_profile_packet(self, ts): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| return packet.profile_packet | |
| def add_clock_snapshot(self, clocks, seq_id=None): | |
| packet = self.add_packet() | |
| if seq_id is not None: | |
| packet.trusted_packet_sequence_id = seq_id | |
| snap = self.packet.clock_snapshot | |
| for k, v in clocks.items(): | |
| clock = snap.clocks.add() | |
| clock.clock_id = k | |
| clock.timestamp = v | |
| def add_gpu_counter_spec(self, | |
| ts, | |
| gpu_id, | |
| counter_id, | |
| name, | |
| description=None, | |
| unit_numerators=[], | |
| unit_denominators=[]): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| gpu_counters = packet.gpu_counter_event | |
| gpu_counters.gpu_id = gpu_id | |
| counter_desc = gpu_counters.counter_descriptor | |
| spec = counter_desc.specs.add() | |
| spec.counter_id = counter_id | |
| spec.name = name | |
| if description is not None: | |
| spec.description = description | |
| spec.numerator_units.extend(unit_numerators) | |
| spec.denominator_units.extend(unit_denominators) | |
| def add_gpu_counter(self, ts, counter_id, value, clock_id=None, seq_id=None): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| if clock_id is not None: | |
| packet.timestamp_clock_id = clock_id | |
| if seq_id is not None: | |
| packet.trusted_packet_sequence_id = seq_id | |
| gpu_counters = self.packet.gpu_counter_event | |
| gpu_counter = gpu_counters.counters.add() | |
| gpu_counter.counter_id = counter_id | |
| gpu_counter.int_value = value | |
| def add_gpu_render_stages_hw_queue_spec(self, specs=[]): | |
| packet = self.add_packet() | |
| spec = self.packet.gpu_render_stage_event.specifications | |
| for s in specs: | |
| hw_queue = spec.hw_queue.add() | |
| hw_queue.name = s.get('name', '') | |
| if 'description' in s: | |
| hw_queue.description = s['description'] | |
| def add_gpu_render_stages_stage_spec(self, specs=[]): | |
| packet = self.add_packet() | |
| spec = self.packet.gpu_render_stage_event.specifications | |
| for s in specs: | |
| stage = spec.stage.add() | |
| stage.name = s.get('name', '') | |
| if 'description' in s: | |
| stage.description = s['description'] | |
| def add_gpu_render_stages(self, | |
| ts, | |
| event_id, | |
| duration, | |
| hw_queue_id, | |
| stage_id, | |
| context, | |
| render_target_handle=None, | |
| render_pass_handle=None, | |
| render_subpass_index_mask=None, | |
| command_buffer_handle=None, | |
| submission_id=None, | |
| extra_data={}): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| render_stage = self.packet.gpu_render_stage_event | |
| render_stage.event_id = event_id | |
| render_stage.duration = duration | |
| render_stage.hw_queue_id = hw_queue_id | |
| render_stage.stage_id = stage_id | |
| render_stage.context = context | |
| if render_target_handle is not None: | |
| render_stage.render_target_handle = render_target_handle | |
| if render_pass_handle is not None: | |
| render_stage.render_pass_handle = render_pass_handle | |
| if render_subpass_index_mask is not None: | |
| for mask in render_subpass_index_mask: | |
| render_stage.render_subpass_index_mask.append(mask) | |
| if command_buffer_handle is not None: | |
| render_stage.command_buffer_handle = command_buffer_handle | |
| if submission_id is not None: | |
| render_stage.submission_id = submission_id | |
| for key, value in extra_data.items(): | |
| data = render_stage.extra_data.add() | |
| data.name = key | |
| if value is not None: | |
| data.value = value | |
| def add_vk_debug_marker(self, ts, pid, vk_device, obj_type, obj, obj_name): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| debug_marker = (self.packet.vulkan_api_event.vk_debug_utils_object_name) | |
| debug_marker.pid = pid | |
| debug_marker.vk_device = vk_device | |
| debug_marker.object_type = obj_type | |
| debug_marker.object = obj | |
| debug_marker.object_name = obj_name | |
| def add_vk_queue_submit(self, ts, dur, pid, tid, vk_queue, vk_command_buffers, | |
| submission_id): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| submit = (self.packet.vulkan_api_event.vk_queue_submit) | |
| submit.duration_ns = dur | |
| submit.pid = pid | |
| submit.tid = tid | |
| for cmd in vk_command_buffers: | |
| submit.vk_command_buffers.append(cmd) | |
| submit.submission_id = submission_id | |
| def add_gpu_log(self, ts, severity, tag, message): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| gpu_log = self.packet.gpu_log | |
| gpu_log.severity = severity | |
| gpu_log.tag = tag | |
| gpu_log.log_message = message | |
| def add_buffer_event_packet(self, ts, buffer_id, layer_name, frame_number, | |
| event_type, duration): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| buffer_event = packet.graphics_frame_event.buffer_event | |
| if buffer_id >= 0: | |
| buffer_event.buffer_id = buffer_id | |
| buffer_event.layer_name = layer_name | |
| buffer_event.frame_number = frame_number | |
| if event_type >= 0: | |
| buffer_event.type = event_type | |
| buffer_event.duration_ns = duration | |
| def add_cpu(self, freqs): | |
| cpu = self.packet.cpu_info.cpus.add() | |
| for freq in freqs: | |
| cpu.frequencies.append(freq) | |
| def add_process_stats(self, pid, freqs): | |
| process = self.packet.process_stats.processes.add() | |
| process.pid = pid | |
| thread = process.threads.add() | |
| thread.tid = pid * 10 | |
| for index in freqs: | |
| thread.cpu_freq_indices.append(index) | |
| thread.cpu_freq_ticks.append(freqs[index]) | |
| def add_gpu_mem_total_ftrace_event(self, ftrace_pid, pid, ts, size): | |
| ftrace = self.__add_ftrace_event(ts, ftrace_pid) | |
| gpu_mem_total_ftrace_event = ftrace.gpu_mem_total | |
| gpu_mem_total_ftrace_event.pid = pid | |
| gpu_mem_total_ftrace_event.size = size | |
| def add_gpu_mem_total_event(self, pid, ts, size): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| gpu_mem_total_event = packet.gpu_mem_total_event | |
| gpu_mem_total_event.pid = pid | |
| gpu_mem_total_event.size = size | |
| def add_sched_blocked_reason(self, ts, pid, io_wait, unblock_pid): | |
| ftrace = self.__add_ftrace_event(ts, unblock_pid) | |
| sched_blocked_reason = ftrace.sched_blocked_reason | |
| sched_blocked_reason.pid = pid | |
| sched_blocked_reason.io_wait = io_wait | |
| def add_track_event(self, | |
| name=None, | |
| ts=None, | |
| track=None, | |
| trusted_sequence_id=0, | |
| cpu_time=None): | |
| packet = self.add_packet(ts=ts) | |
| if name is not None: | |
| packet.track_event.name = name | |
| if track is not None: | |
| packet.track_event.track_uuid = track | |
| packet.trusted_packet_sequence_id = trusted_sequence_id | |
| if cpu_time is not None: | |
| packet.track_event.extra_counter_values.append(cpu_time) | |
| return packet | |
| def add_track_descriptor(self, uuid, parent=None): | |
| packet = self.add_packet() | |
| track_descriptor = packet.track_descriptor | |
| if uuid is not None: | |
| track_descriptor.uuid = uuid | |
| if parent is not None: | |
| track_descriptor.parent_uuid = parent | |
| return packet | |
| def add_process_track_descriptor(self, | |
| process_track, | |
| pid=None, | |
| process_name=None): | |
| packet = self.add_track_descriptor(process_track) | |
| if pid is not None: | |
| packet.track_descriptor.process.pid = pid | |
| if process_name is not None: | |
| packet.track_descriptor.process.process_name = process_name | |
| return packet | |
| def add_thread_track_descriptor(self, | |
| process_track, | |
| thread_track, | |
| trusted_packet_sequence_id=None, | |
| pid=None, | |
| tid=None, | |
| thread_name=None): | |
| packet = self.add_track_descriptor(thread_track, parent=process_track) | |
| if trusted_packet_sequence_id is not None: | |
| packet.trusted_packet_sequence_id = trusted_packet_sequence_id | |
| if pid is not None: | |
| packet.track_descriptor.thread.pid = pid | |
| if tid is not None: | |
| packet.track_descriptor.thread.tid = tid | |
| if thread_name is not None: | |
| packet.track_descriptor.thread.thread_name = thread_name | |
| return packet | |
| def add_trace_packet_defaults(self, | |
| trusted_packet_sequence_id=None, | |
| thread_track=None, | |
| counter_track=None): | |
| packet = self.add_track_descriptor(None) | |
| packet.trusted_packet_sequence_id = trusted_packet_sequence_id | |
| track_event_defaults = packet.trace_packet_defaults.track_event_defaults | |
| track_event_defaults.track_uuid = thread_track | |
| track_event_defaults.extra_counter_track_uuids.append(counter_track) | |
| return packet | |
| def add_counter_track_descriptor(self, | |
| trusted_packet_sequence_id=None, | |
| thread_track=None, | |
| counter_track=None): | |
| packet = self.add_track_descriptor(counter_track, parent=thread_track) | |
| packet.trusted_packet_sequence_id = trusted_packet_sequence_id | |
| packet.track_descriptor.counter.type = self.prototypes.CounterDescriptor \ | |
| .BuiltinCounterType \ | |
| .COUNTER_THREAD_TIME_NS | |
| return packet | |
| def add_track_event_slice_begin(self, | |
| name, | |
| ts, | |
| track=None, | |
| trusted_sequence_id=0, | |
| cpu_time=None): | |
| packet = self.add_track_event( | |
| name, | |
| ts=ts, | |
| track=track, | |
| trusted_sequence_id=trusted_sequence_id, | |
| cpu_time=cpu_time) | |
| packet.track_event.type = self.prototypes.TrackEvent.Type.TYPE_SLICE_BEGIN | |
| return packet | |
| def add_track_event_slice_end(self, | |
| ts, | |
| track=None, | |
| trusted_sequence_id=0, | |
| cpu_time=None): | |
| packet = self.add_track_event( | |
| ts=ts, | |
| track=track, | |
| trusted_sequence_id=trusted_sequence_id, | |
| cpu_time=cpu_time) | |
| packet.track_event.type = self.prototypes.TrackEvent.Type.TYPE_SLICE_END | |
| return packet | |
| # Returns the start slice packet. | |
| def add_track_event_slice(self, | |
| name, | |
| ts, | |
| dur, | |
| track=None, | |
| trusted_sequence_id=0, | |
| cpu_start=None, | |
| cpu_delta=None): | |
| packet = self.add_track_event_slice_begin( | |
| name, | |
| ts, | |
| track=track, | |
| trusted_sequence_id=trusted_sequence_id, | |
| cpu_time=cpu_start) | |
| if dur >= 0: | |
| cpu_end = cpu_start + cpu_delta if cpu_start is not None else None | |
| self.add_track_event_slice_end( | |
| ts + dur, | |
| track=track, | |
| trusted_sequence_id=trusted_sequence_id, | |
| cpu_time=cpu_end) | |
| return packet | |
| def add_rail_mode_slice(self, ts, dur, track, mode): | |
| packet = self.add_track_event_slice( | |
| "Scheduler.RAILMode", ts=ts, dur=dur, track=track) | |
| packet.track_event.chrome_renderer_scheduler_state.rail_mode = mode | |
| def add_latency_info_flow(self, | |
| ts, | |
| dur, | |
| track=None, | |
| trusted_sequence_id=None, | |
| trace_id=None, | |
| step=None, | |
| flow_ids=[], | |
| terminating_flow_ids=[]): | |
| packet = self.add_track_event_slice( | |
| "LatencyInfo.Flow", | |
| ts, | |
| dur, | |
| track=track, | |
| trusted_sequence_id=trusted_sequence_id) | |
| if trace_id is not None: | |
| packet.track_event.chrome_latency_info.trace_id = trace_id | |
| if step is not None: | |
| packet.track_event.chrome_latency_info.step = step | |
| for flow_id in flow_ids: | |
| packet.track_event.flow_ids.append(flow_id) | |
| for flow_id in terminating_flow_ids: | |
| packet.track_event.terminating_flow_ids.append(flow_id) | |
| return packet | |
| def add_input_latency_event_slice(self, | |
| name, | |
| ts, | |
| dur, | |
| track=None, | |
| trace_id=None, | |
| gesture_scroll_id=None, | |
| touch_id=None, | |
| is_coalesced=None, | |
| gets_to_gpu=True): | |
| packet = self.add_track_event_slice( | |
| "InputLatency::" + name, ts=ts, dur=dur, track=track) | |
| latency_info = packet.track_event.chrome_latency_info | |
| latency_info.trace_id = trace_id | |
| if gesture_scroll_id is not None: | |
| latency_info.gesture_scroll_id = gesture_scroll_id | |
| if touch_id is not None: | |
| latency_info.touch_id = touch_id | |
| if gets_to_gpu: | |
| component = latency_info.component_info.add() | |
| component.component_type = self.prototypes \ | |
| .ChromeLatencyInfo.ComponentType \ | |
| .COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER | |
| if is_coalesced is not None: | |
| latency_info.is_coalesced = is_coalesced | |
| return packet | |
| def add_chrome_metadata(self, os_name=None): | |
| metadata = self.add_packet().chrome_events.metadata.add() | |
| if os_name is not None: | |
| metadata.name = "os-name" | |
| metadata.string_value = os_name | |
| return metadata | |
| def add_expected_display_frame_start_event(self, ts, cookie, token, pid): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| event = packet.frame_timeline_event.expected_display_frame_start | |
| if token != -1: | |
| event.cookie = cookie | |
| event.token = token | |
| event.pid = pid | |
| def add_actual_display_frame_start_event(self, ts, cookie, token, pid, | |
| present_type, on_time_finish, | |
| gpu_composition, jank_type, | |
| prediction_type): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| event = packet.frame_timeline_event.actual_display_frame_start | |
| if token != -1: | |
| event.cookie = cookie | |
| event.token = token | |
| event.pid = pid | |
| event.present_type = present_type | |
| event.on_time_finish = on_time_finish | |
| event.gpu_composition = gpu_composition | |
| event.jank_type = jank_type | |
| event.prediction_type = prediction_type | |
| def add_expected_surface_frame_start_event(self, ts, cookie, token, | |
| display_frame_token, pid, | |
| layer_name): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| event = packet.frame_timeline_event.expected_surface_frame_start | |
| if token != -1 and display_frame_token != -1: | |
| event.cookie = cookie | |
| event.token = token | |
| event.display_frame_token = display_frame_token | |
| event.pid = pid | |
| event.layer_name = layer_name | |
| def add_actual_surface_frame_start_event(self, | |
| ts, | |
| cookie, | |
| token, | |
| display_frame_token, | |
| pid, | |
| layer_name, | |
| present_type, | |
| on_time_finish, | |
| gpu_composition, | |
| jank_type, | |
| prediction_type, | |
| jank_severity_type=None): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| event = packet.frame_timeline_event.actual_surface_frame_start | |
| if token != -1 and display_frame_token != -1: | |
| event.cookie = cookie | |
| event.token = token | |
| event.display_frame_token = display_frame_token | |
| event.pid = pid | |
| event.layer_name = layer_name | |
| event.present_type = present_type | |
| event.on_time_finish = on_time_finish | |
| event.gpu_composition = gpu_composition | |
| event.jank_type = jank_type | |
| # jank severity type is not available on every trace. | |
| # When not set, default to none if no jank; otherwise default to unknown | |
| if jank_severity_type is None: | |
| event.jank_severity_type = 1 if event.jank_type == 1 else 0 | |
| else: | |
| event.jank_severity_type = jank_severity_type | |
| event.prediction_type = prediction_type | |
| def add_frame_end_event(self, ts, cookie): | |
| packet = self.add_packet() | |
| packet.timestamp = ts | |
| event = packet.frame_timeline_event.frame_end | |
| event.cookie = cookie | |
| def read_descriptor(file_name): | |
| with open(file_name, 'rb') as f: | |
| contents = f.read() | |
| descriptor = descriptor_pb2.FileDescriptorSet() | |
| descriptor.MergeFromString(contents) | |
| return descriptor | |
| def create_pool(args): | |
| pool = descriptor_pool.DescriptorPool() | |
| for descriptor_file in args.trace_descriptors: | |
| trace_descriptor = read_descriptor(descriptor_file) | |
| for file in trace_descriptor.file: | |
| pool.Add(file) | |
| return pool | |
| def create_trace(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument( | |
| 'trace_descriptors', | |
| type=str, | |
| nargs='+', | |
| help='locations of trace descriptors') | |
| args = parser.parse_args() | |
| pool = create_pool(args) | |
| ProtoTrace = get_message_class( | |
| pool, pool.FindMessageTypeByName('perfetto.protos.Trace')) | |
| class EnumPrototype(object): | |
| def from_descriptor(desc): | |
| res = EnumPrototype() | |
| for desc in desc.values: | |
| setattr(res, desc.name, desc.number) | |
| return res | |
| ChromeEnums = namedtuple('ChromeEnums', [ | |
| 'ProcessType', | |
| 'ThreadType', | |
| ]) | |
| ChromeLatencyInfo = namedtuple('ChromeLatencyInfo', [ | |
| 'ComponentType', | |
| 'Step', | |
| ]) | |
| Prototypes = namedtuple('Prototypes', [ | |
| 'TrackEvent', | |
| 'ChromeEnums', | |
| 'ChromeRAILMode', | |
| 'ChromeLatencyInfo', | |
| 'ChromeProcessDescriptor', | |
| 'ChromeTrackEvent', | |
| 'CounterDescriptor', | |
| 'ThreadDescriptor', | |
| ]) | |
| chrome_latency_info_prototypes = ChromeLatencyInfo( | |
| ComponentType=EnumPrototype.from_descriptor( | |
| pool.FindEnumTypeByName( | |
| 'perfetto.protos.ChromeLatencyInfo.LatencyComponentType')), | |
| Step=EnumPrototype.from_descriptor( | |
| pool.FindEnumTypeByName('perfetto.protos.ChromeLatencyInfo.Step')), | |
| ) | |
| prototypes = Prototypes( | |
| TrackEvent=get_message_class( | |
| pool, pool.FindMessageTypeByName('perfetto.protos.TrackEvent')), | |
| ChromeEnums=ChromeEnums( | |
| ProcessType=EnumPrototype.from_descriptor( | |
| pool.FindEnumTypeByName( | |
| 'perfetto.protos.chrome_enums.ProcessType')), | |
| ThreadType=EnumPrototype.from_descriptor( | |
| pool.FindEnumTypeByName( | |
| 'perfetto.protos.chrome_enums.ThreadType')), | |
| ), | |
| ChromeRAILMode=EnumPrototype.from_descriptor( | |
| pool.FindEnumTypeByName('perfetto.protos.ChromeRAILMode')), | |
| ChromeLatencyInfo=chrome_latency_info_prototypes, | |
| ChromeProcessDescriptor=get_message_class( | |
| pool, | |
| pool.FindMessageTypeByName( | |
| 'perfetto.protos.ChromeProcessDescriptor')), | |
| ChromeTrackEvent=get_message_class( | |
| pool, pool.FindMessageTypeByName('perfetto.protos.ChromeTrackEvent')), | |
| CounterDescriptor=get_message_class( | |
| pool, | |
| pool.FindMessageTypeByName('perfetto.protos.CounterDescriptor')), | |
| ThreadDescriptor=get_message_class( | |
| pool, pool.FindMessageTypeByName('perfetto.protos.ThreadDescriptor')), | |
| ) | |
| return Trace(ProtoTrace(), prototypes) |