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