1#!/usr/bin/python3 2# 3# Copyright (C) 2020 Collabora Ltd 4# 5# Permission is hereby granted, free of charge, to any person obtaining a 6# copy of this software and associated documentation files (the "Software"), 7# to deal in the Software without restriction, including without limitation 8# the rights to use, copy, modify, merge, publish, distribute, sublicense, 9# and/or sell copies of the Software, and to permit persons to whom the 10# Software is furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice shall be included 13# in all copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21# OTHER DEALINGS IN THE SOFTWARE. 22 23from google import protobuf 24import protos.perfetto.trace.perfetto_trace_pb2 25from protos.perfetto.trace.perfetto_trace_pb2 import BUILTIN_CLOCK_BOOTTIME 26from protos.perfetto.trace.perfetto_trace_pb2 import BUILTIN_CLOCK_REALTIME 27import math 28import sys 29import operator 30import time 31 32def add_ftrace_event(out_message, in_packet, in_event, max_host_sequence_id = 0): 33 out_packet = out_message.packet.add() 34 out_packet.ftrace_events.cpu = in_packet.ftrace_events.cpu 35 out_packet.trusted_uid = in_packet.trusted_uid 36 out_packet.trusted_packet_sequence_id += max_host_sequence_id 37 out_packet.ftrace_events.event.add().CopyFrom(in_event) 38 39virtio_gpu_pids = set() 40 41print('%d Loading host trace' % time.time()) 42 43in_message = protos.perfetto.trace.perfetto_trace_pb2.Trace() 44in_message.ParseFromString(open(sys.argv[1], 'rb').read()) 45 46print('%d Copying host trace' % time.time()) 47 48out_message = protos.perfetto.trace.perfetto_trace_pb2.Trace() 49max_host_sequence_id = 0 50first_host_virtio_gpu_cmd = math.inf 51host_boot_ts = -1 52for in_packet in in_message.packet: 53 max_host_sequence_id = max(max_host_sequence_id, 54 in_packet.trusted_packet_sequence_id) 55 56 if in_packet.HasField('ftrace_events'): 57 for event in in_packet.ftrace_events.event: 58 if event.HasField('sched_switch'): 59 if 'virtio_gpu' == event.sched_switch.prev_comm: 60 virtio_gpu_pids.add(event.sched_switch.prev_pid) 61 if 'virtio_gpu' == event.sched_switch.next_comm: 62 virtio_gpu_pids.add(event.sched_switch.next_pid) 63 64 if event.sched_switch.prev_pid in virtio_gpu_pids or \ 65 event.sched_switch.next_pid in virtio_gpu_pids: 66 add_ftrace_event(out_message, in_packet, event) 67 elif event.HasField('sched_wakeup'): 68 if 'virtio_gpu' == event.sched_wakeup.comm: 69 virtio_gpu_pids.add(event.sched_wakeup.pid) 70 71 if event.sched_wakeup.pid in virtio_gpu_pids: 72 add_ftrace_event(out_message, in_packet, event) 73 elif event.HasField('print'): 74 event_type, guest_pid, label, cookie = event.print.buf.split('|') 75 76 # Replace host PID with the guest PID 77 event.pid = int(guest_pid) 78 add_ftrace_event(out_message, in_packet, event) 79 else: 80 if in_packet.HasField('track_descriptor'): 81 if in_packet.track_descriptor.HasField('name'): 82 in_packet.track_descriptor.name += ' (Host)' 83 elif in_packet.HasField('track_event'): 84 if in_packet.track_event.type == in_packet.track_event.TYPE_SLICE_BEGIN and \ 85 in_packet.track_event.name == 'GetCapset': 86 first_host_virtio_gpu_cmd = min(first_host_virtio_gpu_cmd, in_packet.timestamp) 87 elif host_boot_ts == -1 and in_packet.HasField('clock_snapshot'): 88 for clock in in_packet.clock_snapshot.clocks: 89 if clock.clock_id == BUILTIN_CLOCK_BOOTTIME: 90 host_boottime = clock.timestamp 91 elif clock.clock_id == BUILTIN_CLOCK_REALTIME: 92 host_realtime = clock.timestamp 93 host_boot_ts = host_realtime - host_boottime 94 out_packet = out_message.packet.add() 95 out_packet.CopyFrom(in_packet) 96 97print('%d Loading guest trace' % time.time()) 98in_message.ParseFromString(open(sys.argv[2], 'rb').read()) 99 100#print('%d Writing guest trace txt' % time.time()) 101#open('../traces-db/perfetto-guest.txt', 'w').write(str(in_message)) 102 103first_guest_virtio_gpu_cmd = math.inf 104guest_boot_ts = -1 105for in_packet in in_message.packet: 106 if guest_boot_ts == -1 and in_packet.HasField('clock_snapshot'): 107 for clock in in_packet.clock_snapshot.clocks: 108 if clock.clock_id == BUILTIN_CLOCK_BOOTTIME: 109 guest_boottime = clock.timestamp 110 elif clock.clock_id == BUILTIN_CLOCK_REALTIME: 111 guest_realtime = clock.timestamp 112 guest_boot_ts = guest_realtime - guest_boottime 113 elif in_packet.HasField('track_event'): 114 if in_packet.track_event.type == in_packet.track_event.TYPE_SLICE_BEGIN and \ 115 in_packet.track_event.name == 'DRM_IOCTL_VIRTGPU_GET_CAPS': 116 first_guest_virtio_gpu_cmd = min(first_guest_virtio_gpu_cmd, in_packet.timestamp) 117 118delta = guest_boot_ts - host_boot_ts 119cmd_delta = first_host_virtio_gpu_cmd - first_guest_virtio_gpu_cmd - delta 120print("boottime delta %ds." % (delta / 1000 / 1000 / 1000)) 121print("cmd delta %dus." % (cmd_delta / 1000)) 122 123for in_packet in in_message.packet: 124 if in_packet.HasField('process_tree') or \ 125 in_packet.HasField('service_event') or \ 126 in_packet.HasField('track_event') or \ 127 in_packet.HasField('trace_packet_defaults') or \ 128 in_packet.HasField('track_descriptor'): 129 out_packet = out_message.packet.add() 130 out_packet.CopyFrom(in_packet) 131 out_packet.trusted_packet_sequence_id += max_host_sequence_id 132 out_packet.timestamp += delta 133 if out_packet.HasField('track_descriptor'): 134 if out_packet.track_descriptor.HasField('name'): 135 out_packet.track_descriptor.name += ' (Guest)' 136 elif in_packet.HasField('ftrace_events'): 137 for event in in_packet.ftrace_events.event: 138 event.timestamp += delta 139 add_ftrace_event(out_message, in_packet, event, max_host_sequence_id) 140 141def get_timestamp(packet): 142 if packet.HasField('timestamp'): 143 return packet.timestamp 144 elif packet.HasField('ftrace_events') and \ 145 packet.ftrace_events.event: 146 return packet.ftrace_events.event[0].timestamp 147 return 0 148 149out_message.packet.sort(key=get_timestamp) 150print('%d Writing merged trace' % time.time()) 151open(sys.argv[3], 'wb').write(out_message.SerializeToString()) 152 153#print('%d Writing merged trace txt' % time.time()) 154#open('../traces-db/perfetto.txt', 'w').write(str(out_message)) 155