1#!/usr/bin/env python3 2# 3# Dump the contents of a recorded execution stream 4# 5# Copyright (c) 2017 Alex Bennée <alex.bennee@linaro.org> 6# 7# This library is free software; you can redistribute it and/or 8# modify it under the terms of the GNU Lesser General Public 9# License as published by the Free Software Foundation; either 10# version 2.1 of the License, or (at your option) any later version. 11# 12# This library is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# Lesser General Public License for more details. 16# 17# You should have received a copy of the GNU Lesser General Public 18# License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 20import argparse 21import struct 22import os 23import sys 24from collections import namedtuple 25from os import path 26 27# This mirrors some of the global replay state which some of the 28# stream loading refers to. Some decoders may read the next event so 29# we need handle that case. Calling reuse_event will ensure the next 30# event is read from the cache rather than advancing the file. 31 32class ReplayState(object): 33 def __init__(self): 34 self.event = -1 35 self.event_count = 0 36 self.already_read = False 37 self.current_checkpoint = 0 38 self.checkpoint = 0 39 40 def set_event(self, ev): 41 self.event = ev 42 self.event_count += 1 43 44 def get_event(self): 45 self.already_read = False 46 return self.event 47 48 def reuse_event(self, ev): 49 self.event = ev 50 self.already_read = True 51 52 def set_checkpoint(self): 53 self.checkpoint = self.event - self.checkpoint_start 54 55 def get_checkpoint(self): 56 return self.checkpoint 57 58replay_state = ReplayState() 59 60# Simple read functions that mirror replay-internal.c 61# The file-stream is big-endian and manually written out a byte at a time. 62 63def read_byte(fin): 64 "Read a single byte" 65 return struct.unpack('>B', fin.read(1))[0] 66 67def read_event(fin): 68 "Read a single byte event, but save some state" 69 if replay_state.already_read: 70 return replay_state.get_event() 71 else: 72 replay_state.set_event(read_byte(fin)) 73 return replay_state.event 74 75def read_word(fin): 76 "Read a 16 bit word" 77 return struct.unpack('>H', fin.read(2))[0] 78 79def read_dword(fin): 80 "Read a 32 bit word" 81 return struct.unpack('>I', fin.read(4))[0] 82 83def read_qword(fin): 84 "Read a 64 bit word" 85 return struct.unpack('>Q', fin.read(8))[0] 86 87def read_array(fin): 88 "Read a sized array" 89 size = read_dword(fin) 90 data = fin.read(size) 91 return data 92 93# Generic decoder structure 94Decoder = namedtuple("Decoder", "eid name fn") 95 96def call_decode(table, index, dumpfile): 97 "Search decode table for next step" 98 decoder = next((d for d in table if d.eid == index), None) 99 if not decoder: 100 print("Could not decode index: %d" % (index)) 101 print("Entry is: %s" % (decoder)) 102 print("Decode Table is:\n%s" % (table)) 103 raise(Exception("unknown event")) 104 else: 105 return decoder.fn(decoder.eid, decoder.name, dumpfile) 106 107# Print event 108def print_event(eid, name, string=None, event_count=None): 109 "Print event with count" 110 if not event_count: 111 event_count = replay_state.event_count 112 113 if string: 114 print("%d:%s(%d) %s" % (event_count, name, eid, string)) 115 else: 116 print("%d:%s(%d)" % (event_count, name, eid)) 117 118 119# Decoders for each event type 120 121def decode_unimp(eid, name, _unused_dumpfile): 122 "Unimplemented decoder, will trigger exit" 123 print("%s not handled - will now stop" % (name)) 124 raise(Exception("unhandled event")) 125 126def decode_plain(eid, name, _unused_dumpfile): 127 "Plain events without additional data" 128 print_event(eid, name, "no data") 129 return True 130 131# Checkpoint decoder 132def swallow_async_qword(eid, name, dumpfile): 133 "Swallow a qword of data without looking at it" 134 step_id = read_qword(dumpfile) 135 print(" %s(%d) @ %d" % (name, eid, step_id)) 136 return True 137 138def swallow_bytes(eid, name, dumpfile, nr): 139 """Swallow nr bytes of data without looking at it""" 140 dumpfile.seek(nr, os.SEEK_CUR) 141 142total_insns = 0 143 144def decode_instruction(eid, name, dumpfile): 145 global total_insns 146 ins_diff = read_dword(dumpfile) 147 total_insns += ins_diff 148 print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns)) 149 return True 150 151def decode_interrupt(eid, name, dumpfile): 152 print_event(eid, name) 153 return True 154 155def decode_exception(eid, name, dumpfile): 156 print_event(eid, name) 157 return True 158 159# v12 does away with the additional event byte and encodes it in the main type 160# Between v8 and v9, REPLAY_ASYNC_BH_ONESHOT was added, but we don't decode 161# those versions so leave it out. 162async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), 163 Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp), 164 Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp), 165 Decoder(3, "REPLAY_ASYNC_CHAR_READ", decode_unimp), 166 Decoder(4, "REPLAY_ASYNC_EVENT_BLOCK", decode_unimp), 167 Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp), 168] 169# See replay_read_events/replay_read_event 170def decode_async_old(eid, name, dumpfile): 171 """Decode an ASYNC event (pre-v8)""" 172 173 print_event(eid, name) 174 175 async_event_kind = read_byte(dumpfile) 176 async_event_checkpoint = read_byte(dumpfile) 177 178 if async_event_checkpoint != replay_state.current_checkpoint: 179 print(" mismatch between checkpoint %d and async data %d" % ( 180 replay_state.current_checkpoint, async_event_checkpoint)) 181 return True 182 183 return call_decode(async_decode_table, async_event_kind, dumpfile) 184 185def decode_async_bh(eid, name, dumpfile): 186 op_id = read_qword(dumpfile) 187 print_event(eid, name) 188 return True 189 190def decode_async_bh_oneshot(eid, name, dumpfile): 191 op_id = read_qword(dumpfile) 192 print_event(eid, name) 193 return True 194 195def decode_async_char_read(eid, name, dumpfile): 196 char_id = read_byte(dumpfile) 197 size = read_dword(dumpfile) 198 print_event(eid, name, "device:%x chars:%s" % (char_id, dumpfile.read(size))) 199 return True 200 201def decode_async_block(eid, name, dumpfile): 202 op_id = read_qword(dumpfile) 203 print_event(eid, name) 204 return True 205 206def decode_async_net(eid, name, dumpfile): 207 net_id = read_byte(dumpfile) 208 flags = read_dword(dumpfile) 209 size = read_dword(dumpfile) 210 swallow_bytes(eid, name, dumpfile, size) 211 print_event(eid, name, "net:%x flags:%x bytes:%d" % (net_id, flags, size)) 212 return True 213 214def decode_shutdown(eid, name, dumpfile): 215 print_event(eid, name) 216 return True 217 218def decode_char_write(eid, name, dumpfile): 219 res = read_dword(dumpfile) 220 offset = read_dword(dumpfile) 221 print_event(eid, name, "%d -> %d" % (offset, res)) 222 return True 223 224def decode_audio_out(eid, name, dumpfile): 225 audio_data = read_dword(dumpfile) 226 print_event(eid, name, "%d" % (audio_data)) 227 return True 228 229def decode_random(eid, name, dumpfile): 230 ret = read_dword(dumpfile) 231 size = read_dword(dumpfile) 232 swallow_bytes(eid, name, dumpfile, size) 233 if (ret): 234 print_event(eid, name, "%d bytes (getrandom failed)" % (size)) 235 else: 236 print_event(eid, name, "%d bytes" % (size)) 237 return True 238 239def decode_clock(eid, name, dumpfile): 240 clock_data = read_qword(dumpfile) 241 print_event(eid, name, "0x%x" % (clock_data)) 242 return True 243 244def __decode_checkpoint(eid, name, dumpfile, old): 245 """Decode a checkpoint. 246 247 Checkpoints contain a series of async events with their own specific data. 248 """ 249 replay_state.set_checkpoint() 250 # save event count as we peek ahead 251 event_number = replay_state.event_count 252 next_event = read_event(dumpfile) 253 254 # if the next event is EVENT_ASYNC there are a bunch of 255 # async events to read, otherwise we are done 256 if (old and next_event == 3) or (not old and next_event >= 3 and next_event <= 9): 257 print_event(eid, name, "more data follows", event_number) 258 else: 259 print_event(eid, name, "no additional data", event_number) 260 261 replay_state.reuse_event(next_event) 262 return True 263 264def decode_checkpoint_old(eid, name, dumpfile): 265 return __decode_checkpoint(eid, name, dumpfile, False) 266 267def decode_checkpoint(eid, name, dumpfile): 268 return __decode_checkpoint(eid, name, dumpfile, True) 269 270def decode_checkpoint_init(eid, name, dumpfile): 271 print_event(eid, name) 272 return True 273 274def decode_end(eid, name, dumpfile): 275 print_event(eid, name) 276 return False 277 278# pre-MTTCG merge 279v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 280 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 281 Decoder(2, "EVENT_EXCEPTION", decode_plain), 282 Decoder(3, "EVENT_ASYNC", decode_async_old), 283 Decoder(4, "EVENT_SHUTDOWN", decode_unimp), 284 Decoder(5, "EVENT_CHAR_WRITE", decode_char_write), 285 Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), 286 Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 287 Decoder(8, "EVENT_CLOCK_HOST", decode_clock), 288 Decoder(9, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 289 Decoder(10, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 290 Decoder(11, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 291 Decoder(12, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 292 Decoder(13, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 293 Decoder(14, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 294 Decoder(15, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 295 Decoder(16, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 296 Decoder(17, "EVENT_CP_INIT", decode_checkpoint_init), 297 Decoder(18, "EVENT_CP_RESET", decode_checkpoint), 298] 299 300# post-MTTCG merge, AUDIO support added 301v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 302 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 303 Decoder(2, "EVENT_EXCEPTION", decode_plain), 304 Decoder(3, "EVENT_ASYNC", decode_async_old), 305 Decoder(4, "EVENT_SHUTDOWN", decode_unimp), 306 Decoder(5, "EVENT_CHAR_WRITE", decode_char_write), 307 Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), 308 Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 309 Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out), 310 Decoder(9, "EVENT_AUDIO_IN", decode_unimp), 311 Decoder(10, "EVENT_CLOCK_HOST", decode_clock), 312 Decoder(11, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 313 Decoder(12, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 314 Decoder(13, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 315 Decoder(14, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 316 Decoder(15, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 317 Decoder(16, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 318 Decoder(17, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 319 Decoder(18, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 320 Decoder(19, "EVENT_CP_INIT", decode_checkpoint_init), 321 Decoder(20, "EVENT_CP_RESET", decode_checkpoint), 322] 323 324# Shutdown cause added 325v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 326 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 327 Decoder(2, "EVENT_EXCEPTION", decode_unimp), 328 Decoder(3, "EVENT_ASYNC", decode_async_old), 329 Decoder(4, "EVENT_SHUTDOWN", decode_unimp), 330 Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), 331 Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp), 332 Decoder(7, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp), 333 Decoder(8, "EVENT_SHUTDOWN_HOST_UI", decode_unimp), 334 Decoder(9, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp), 335 Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp), 336 Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp), 337 Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp), 338 Decoder(13, "EVENT_CHAR_WRITE", decode_char_write), 339 Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp), 340 Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 341 Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out), 342 Decoder(17, "EVENT_AUDIO_IN", decode_unimp), 343 Decoder(18, "EVENT_CLOCK_HOST", decode_clock), 344 Decoder(19, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 345 Decoder(20, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 346 Decoder(21, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 347 Decoder(22, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 348 Decoder(23, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 349 Decoder(24, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 350 Decoder(25, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 351 Decoder(26, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 352 Decoder(27, "EVENT_CP_INIT", decode_checkpoint_init), 353 Decoder(28, "EVENT_CP_RESET", decode_checkpoint), 354] 355 356v12_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 357 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 358 Decoder(2, "EVENT_EXCEPTION", decode_exception), 359 Decoder(3, "EVENT_ASYNC_BH", decode_async_bh), 360 Decoder(4, "EVENT_ASYNC_BH_ONESHOT", decode_async_bh_oneshot), 361 Decoder(5, "EVENT_ASYNC_INPUT", decode_unimp), 362 Decoder(6, "EVENT_ASYNC_INPUT_SYNC", decode_unimp), 363 Decoder(7, "EVENT_ASYNC_CHAR_READ", decode_async_char_read), 364 Decoder(8, "EVENT_ASYNC_BLOCK", decode_async_block), 365 Decoder(9, "EVENT_ASYNC_NET", decode_async_net), 366 Decoder(10, "EVENT_SHUTDOWN", decode_shutdown), 367 Decoder(11, "EVENT_SHUTDOWN_HOST_ERR", decode_shutdown), 368 Decoder(12, "EVENT_SHUTDOWN_HOST_QMP_QUIT", decode_shutdown), 369 Decoder(13, "EVENT_SHUTDOWN_HOST_QMP_RESET", decode_shutdown), 370 Decoder(14, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_shutdown), 371 Decoder(15, "EVENT_SHUTDOWN_HOST_UI", decode_shutdown), 372 Decoder(16, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_shutdown), 373 Decoder(17, "EVENT_SHUTDOWN_GUEST_RESET", decode_shutdown), 374 Decoder(18, "EVENT_SHUTDOWN_GUEST_PANIC", decode_shutdown), 375 Decoder(19, "EVENT_SHUTDOWN_SUBSYS_RESET", decode_shutdown), 376 Decoder(20, "EVENT_SHUTDOWN_SNAPSHOT_LOAD", decode_shutdown), 377 Decoder(21, "EVENT_SHUTDOWN___MAX", decode_shutdown), 378 Decoder(22, "EVENT_CHAR_WRITE", decode_char_write), 379 Decoder(23, "EVENT_CHAR_READ_ALL", decode_unimp), 380 Decoder(24, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 381 Decoder(25, "EVENT_AUDIO_OUT", decode_audio_out), 382 Decoder(26, "EVENT_AUDIO_IN", decode_unimp), 383 Decoder(27, "EVENT_RANDOM", decode_random), 384 Decoder(28, "EVENT_CLOCK_HOST", decode_clock), 385 Decoder(29, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 386 Decoder(30, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 387 Decoder(31, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 388 Decoder(32, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 389 Decoder(33, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 390 Decoder(34, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 391 Decoder(35, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 392 Decoder(36, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 393 Decoder(37, "EVENT_CP_INIT", decode_checkpoint_init), 394 Decoder(38, "EVENT_CP_RESET", decode_checkpoint), 395 Decoder(39, "EVENT_END", decode_end), 396] 397 398def parse_arguments(): 399 "Grab arguments for script" 400 parser = argparse.ArgumentParser() 401 parser.add_argument("-f", "--file", help='record/replay dump to read from', 402 required=True) 403 return parser.parse_args() 404 405def decode_file(filename): 406 "Decode a record/replay dump" 407 dumpfile = open(filename, "rb") 408 dumpsize = path.getsize(filename) 409 # read and throwaway the header 410 version = read_dword(dumpfile) 411 junk = read_qword(dumpfile) 412 413 # see REPLAY_VERSION 414 print("HEADER: version 0x%x" % (version)) 415 416 if version == 0xe0200c: 417 event_decode_table = v12_event_table 418 replay_state.checkpoint_start = 30 419 elif version == 0xe02007: 420 event_decode_table = v7_event_table 421 replay_state.checkpoint_start = 12 422 elif version == 0xe02006: 423 event_decode_table = v6_event_table 424 replay_state.checkpoint_start = 12 425 else: 426 event_decode_table = v5_event_table 427 replay_state.checkpoint_start = 10 428 429 try: 430 decode_ok = True 431 while decode_ok: 432 event = read_event(dumpfile) 433 decode_ok = call_decode(event_decode_table, event, 434 dumpfile) 435 except Exception as inst: 436 print(f"error {inst}") 437 sys.exit(1) 438 439 finally: 440 print(f"Reached {dumpfile.tell()} of {dumpsize} bytes") 441 dumpfile.close() 442 443if __name__ == "__main__": 444 args = parse_arguments() 445 decode_file(args.file) 446