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 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 23from collections import namedtuple 24 25# This mirrors some of the global replay state which some of the 26# stream loading refers to. Some decoders may read the next event so 27# we need handle that case. Calling reuse_event will ensure the next 28# event is read from the cache rather than advancing the file. 29 30class ReplayState(object): 31 def __init__(self): 32 self.event = -1 33 self.event_count = 0 34 self.already_read = False 35 self.current_checkpoint = 0 36 self.checkpoint = 0 37 38 def set_event(self, ev): 39 self.event = ev 40 self.event_count += 1 41 42 def get_event(self): 43 self.already_read = False 44 return self.event 45 46 def reuse_event(self, ev): 47 self.event = ev 48 self.already_read = True 49 50 def set_checkpoint(self): 51 self.checkpoint = self.event - self.checkpoint_start 52 53 def get_checkpoint(self): 54 return self.checkpoint 55 56replay_state = ReplayState() 57 58# Simple read functions that mirror replay-internal.c 59# The file-stream is big-endian and manually written out a byte at a time. 60 61def read_byte(fin): 62 "Read a single byte" 63 return struct.unpack('>B', fin.read(1))[0] 64 65def read_event(fin): 66 "Read a single byte event, but save some state" 67 if replay_state.already_read: 68 return replay_state.get_event() 69 else: 70 replay_state.set_event(read_byte(fin)) 71 return replay_state.event 72 73def read_word(fin): 74 "Read a 16 bit word" 75 return struct.unpack('>H', fin.read(2))[0] 76 77def read_dword(fin): 78 "Read a 32 bit word" 79 return struct.unpack('>I', fin.read(4))[0] 80 81def read_qword(fin): 82 "Read a 64 bit word" 83 return struct.unpack('>Q', fin.read(8))[0] 84 85# Generic decoder structure 86Decoder = namedtuple("Decoder", "eid name fn") 87 88def call_decode(table, index, dumpfile): 89 "Search decode table for next step" 90 decoder = next((d for d in table if d.eid == index), None) 91 if not decoder: 92 print("Could not decode index: %d" % (index)) 93 print("Entry is: %s" % (decoder)) 94 print("Decode Table is:\n%s" % (table)) 95 return False 96 else: 97 return decoder.fn(decoder.eid, decoder.name, dumpfile) 98 99# Print event 100def print_event(eid, name, string=None, event_count=None): 101 "Print event with count" 102 if not event_count: 103 event_count = replay_state.event_count 104 105 if string: 106 print("%d:%s(%d) %s" % (event_count, name, eid, string)) 107 else: 108 print("%d:%s(%d)" % (event_count, name, eid)) 109 110 111# Decoders for each event type 112 113def decode_unimp(eid, name, _unused_dumpfile): 114 "Unimplimented decoder, will trigger exit" 115 print("%s not handled - will now stop" % (name)) 116 return False 117 118# Checkpoint decoder 119def swallow_async_qword(eid, name, dumpfile): 120 "Swallow a qword of data without looking at it" 121 step_id = read_qword(dumpfile) 122 print(" %s(%d) @ %d" % (name, eid, step_id)) 123 return True 124 125async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), 126 Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp), 127 Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp), 128 Decoder(3, "REPLAY_ASYNC_CHAR_READ", decode_unimp), 129 Decoder(4, "REPLAY_ASYNC_EVENT_BLOCK", decode_unimp), 130 Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp), 131] 132# See replay_read_events/replay_read_event 133def decode_async(eid, name, dumpfile): 134 """Decode an ASYNC event""" 135 136 print_event(eid, name) 137 138 async_event_kind = read_byte(dumpfile) 139 async_event_checkpoint = read_byte(dumpfile) 140 141 if async_event_checkpoint != replay_state.current_checkpoint: 142 print(" mismatch between checkpoint %d and async data %d" % ( 143 replay_state.current_checkpoint, async_event_checkpoint)) 144 return True 145 146 return call_decode(async_decode_table, async_event_kind, dumpfile) 147 148 149def decode_instruction(eid, name, dumpfile): 150 ins_diff = read_dword(dumpfile) 151 print_event(eid, name, "0x%x" % (ins_diff)) 152 return True 153 154def decode_audio_out(eid, name, dumpfile): 155 audio_data = read_dword(dumpfile) 156 print_event(eid, name, "%d" % (audio_data)) 157 return True 158 159def decode_checkpoint(eid, name, dumpfile): 160 """Decode a checkpoint. 161 162 Checkpoints contain a series of async events with their own specific data. 163 """ 164 replay_state.set_checkpoint() 165 # save event count as we peek ahead 166 event_number = replay_state.event_count 167 next_event = read_event(dumpfile) 168 169 # if the next event is EVENT_ASYNC there are a bunch of 170 # async events to read, otherwise we are done 171 if next_event != 3: 172 print_event(eid, name, "no additional data", event_number) 173 else: 174 print_event(eid, name, "more data follows", event_number) 175 176 replay_state.reuse_event(next_event) 177 return True 178 179def decode_checkpoint_init(eid, name, dumpfile): 180 print_event(eid, name) 181 return True 182 183def decode_interrupt(eid, name, dumpfile): 184 print_event(eid, name) 185 return True 186 187def decode_clock(eid, name, dumpfile): 188 clock_data = read_qword(dumpfile) 189 print_event(eid, name, "0x%x" % (clock_data)) 190 return True 191 192 193# pre-MTTCG merge 194v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 195 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 196 Decoder(2, "EVENT_EXCEPTION", decode_unimp), 197 Decoder(3, "EVENT_ASYNC", decode_async), 198 Decoder(4, "EVENT_SHUTDOWN", decode_unimp), 199 Decoder(5, "EVENT_CHAR_WRITE", decode_unimp), 200 Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), 201 Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 202 Decoder(8, "EVENT_CLOCK_HOST", decode_clock), 203 Decoder(9, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 204 Decoder(10, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 205 Decoder(11, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 206 Decoder(12, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 207 Decoder(13, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 208 Decoder(14, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 209 Decoder(15, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 210 Decoder(16, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 211 Decoder(17, "EVENT_CP_INIT", decode_checkpoint_init), 212 Decoder(18, "EVENT_CP_RESET", decode_checkpoint), 213] 214 215# post-MTTCG merge, AUDIO support added 216v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 217 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 218 Decoder(2, "EVENT_EXCEPTION", decode_unimp), 219 Decoder(3, "EVENT_ASYNC", decode_async), 220 Decoder(4, "EVENT_SHUTDOWN", decode_unimp), 221 Decoder(5, "EVENT_CHAR_WRITE", decode_unimp), 222 Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), 223 Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 224 Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out), 225 Decoder(9, "EVENT_AUDIO_IN", decode_unimp), 226 Decoder(10, "EVENT_CLOCK_HOST", decode_clock), 227 Decoder(11, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 228 Decoder(12, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 229 Decoder(13, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 230 Decoder(14, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 231 Decoder(15, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 232 Decoder(16, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 233 Decoder(17, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 234 Decoder(18, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 235 Decoder(19, "EVENT_CP_INIT", decode_checkpoint_init), 236 Decoder(20, "EVENT_CP_RESET", decode_checkpoint), 237] 238 239# Shutdown cause added 240v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), 241 Decoder(1, "EVENT_INTERRUPT", decode_interrupt), 242 Decoder(2, "EVENT_EXCEPTION", decode_unimp), 243 Decoder(3, "EVENT_ASYNC", decode_async), 244 Decoder(4, "EVENT_SHUTDOWN", decode_unimp), 245 Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), 246 Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp), 247 Decoder(7, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp), 248 Decoder(8, "EVENT_SHUTDOWN_HOST_UI", decode_unimp), 249 Decoder(9, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp), 250 Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp), 251 Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp), 252 Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp), 253 Decoder(13, "EVENT_CHAR_WRITE", decode_unimp), 254 Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp), 255 Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), 256 Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out), 257 Decoder(17, "EVENT_AUDIO_IN", decode_unimp), 258 Decoder(18, "EVENT_CLOCK_HOST", decode_clock), 259 Decoder(19, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), 260 Decoder(20, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), 261 Decoder(21, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), 262 Decoder(22, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), 263 Decoder(23, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), 264 Decoder(24, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), 265 Decoder(25, "EVENT_CP_CLOCK_HOST", decode_checkpoint), 266 Decoder(26, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), 267 Decoder(27, "EVENT_CP_INIT", decode_checkpoint_init), 268 Decoder(28, "EVENT_CP_RESET", decode_checkpoint), 269] 270 271def parse_arguments(): 272 "Grab arguments for script" 273 parser = argparse.ArgumentParser() 274 parser.add_argument("-f", "--file", help='record/replay dump to read from', 275 required=True) 276 return parser.parse_args() 277 278def decode_file(filename): 279 "Decode a record/replay dump" 280 dumpfile = open(filename, "rb") 281 282 # read and throwaway the header 283 version = read_dword(dumpfile) 284 junk = read_qword(dumpfile) 285 286 print("HEADER: version 0x%x" % (version)) 287 288 if version == 0xe02007: 289 event_decode_table = v7_event_table 290 replay_state.checkpoint_start = 12 291 elif version == 0xe02006: 292 event_decode_table = v6_event_table 293 replay_state.checkpoint_start = 12 294 else: 295 event_decode_table = v5_event_table 296 replay_state.checkpoint_start = 10 297 298 try: 299 decode_ok = True 300 while decode_ok: 301 event = read_event(dumpfile) 302 decode_ok = call_decode(event_decode_table, event, dumpfile) 303 finally: 304 dumpfile.close() 305 306if __name__ == "__main__": 307 args = parse_arguments() 308 decode_file(args.file) 309