183de0ea4SMarc-André Lureau#!/usr/bin/env python3 2b1742570SAlexander Graf# 3b1742570SAlexander Graf# Migration Stream Analyzer 4b1742570SAlexander Graf# 5b1742570SAlexander Graf# Copyright (c) 2015 Alexander Graf <agraf@suse.de> 6b1742570SAlexander Graf# 7b1742570SAlexander Graf# This library is free software; you can redistribute it and/or 8b1742570SAlexander Graf# modify it under the terms of the GNU Lesser General Public 9b1742570SAlexander Graf# License as published by the Free Software Foundation; either 1061f3c91aSChetan Pant# version 2.1 of the License, or (at your option) any later version. 11b1742570SAlexander Graf# 12b1742570SAlexander Graf# This library is distributed in the hope that it will be useful, 13b1742570SAlexander Graf# but WITHOUT ANY WARRANTY; without even the implied warranty of 14b1742570SAlexander Graf# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15b1742570SAlexander Graf# Lesser General Public License for more details. 16b1742570SAlexander Graf# 17b1742570SAlexander Graf# You should have received a copy of the GNU Lesser General Public 18b1742570SAlexander Graf# License along with this library; if not, see <http://www.gnu.org/licenses/>. 19b1742570SAlexander Graf 20b1742570SAlexander Grafimport json 21b1742570SAlexander Grafimport os 22b1742570SAlexander Grafimport argparse 23b1742570SAlexander Grafimport collections 2483de0ea4SMarc-André Lureauimport struct 2583de0ea4SMarc-André Lureauimport sys 2683de0ea4SMarc-André Lureau 2783de0ea4SMarc-André Lureau 28b1742570SAlexander Grafdef mkdir_p(path): 29b1742570SAlexander Graf try: 30b1742570SAlexander Graf os.makedirs(path) 31b1742570SAlexander Graf except OSError: 32b1742570SAlexander Graf pass 33b1742570SAlexander Graf 3483de0ea4SMarc-André Lureau 35b1742570SAlexander Grafclass MigrationFile(object): 36b1742570SAlexander Graf def __init__(self, filename): 37b1742570SAlexander Graf self.filename = filename 38b1742570SAlexander Graf self.file = open(self.filename, "rb") 39b1742570SAlexander Graf 40b1742570SAlexander Graf def read64(self): 41caea0327SFabiano Rosas return int.from_bytes(self.file.read(8), byteorder='big', signed=False) 42b1742570SAlexander Graf 43b1742570SAlexander Graf def read32(self): 44caea0327SFabiano Rosas return int.from_bytes(self.file.read(4), byteorder='big', signed=False) 45b1742570SAlexander Graf 46b1742570SAlexander Graf def read16(self): 47caea0327SFabiano Rosas return int.from_bytes(self.file.read(2), byteorder='big', signed=False) 48b1742570SAlexander Graf 49b1742570SAlexander Graf def read8(self): 5083de0ea4SMarc-André Lureau return int.from_bytes(self.file.read(1), byteorder='big', signed=True) 51b1742570SAlexander Graf 52b1742570SAlexander Graf def readstr(self, len = None): 5383de0ea4SMarc-André Lureau return self.readvar(len).decode('utf-8') 54b1742570SAlexander Graf 55b1742570SAlexander Graf def readvar(self, size = None): 56b1742570SAlexander Graf if size is None: 57b1742570SAlexander Graf size = self.read8() 58b1742570SAlexander Graf if size == 0: 59b1742570SAlexander Graf return "" 60b1742570SAlexander Graf value = self.file.read(size) 61b1742570SAlexander Graf if len(value) != size: 62b1742570SAlexander Graf raise Exception("Unexpected end of %s at 0x%x" % (self.filename, self.file.tell())) 63b1742570SAlexander Graf return value 64b1742570SAlexander Graf 65b1742570SAlexander Graf def tell(self): 66b1742570SAlexander Graf return self.file.tell() 67b1742570SAlexander Graf 68e3839b0cSFabiano Rosas def seek(self, a, b): 69e3839b0cSFabiano Rosas return self.file.seek(a, b) 70e3839b0cSFabiano Rosas 71b1742570SAlexander Graf # The VMSD description is at the end of the file, after EOF. Look for 72b1742570SAlexander Graf # the last NULL byte, then for the beginning brace of JSON. 73b1742570SAlexander Graf def read_migration_debug_json(self): 74b1742570SAlexander Graf QEMU_VM_VMDESCRIPTION = 0x06 75b1742570SAlexander Graf 76b1742570SAlexander Graf # Remember the offset in the file when we started 77b1742570SAlexander Graf entrypos = self.file.tell() 78b1742570SAlexander Graf 79b1742570SAlexander Graf # Read the last 10MB 80b1742570SAlexander Graf self.file.seek(0, os.SEEK_END) 81b1742570SAlexander Graf endpos = self.file.tell() 82b1742570SAlexander Graf self.file.seek(max(-endpos, -10 * 1024 * 1024), os.SEEK_END) 83b1742570SAlexander Graf datapos = self.file.tell() 84b1742570SAlexander Graf data = self.file.read() 85b1742570SAlexander Graf # The full file read closed the file as well, reopen it 86b1742570SAlexander Graf self.file = open(self.filename, "rb") 87b1742570SAlexander Graf 88b1742570SAlexander Graf # Find the last NULL byte, then the first brace after that. This should 89b1742570SAlexander Graf # be the beginning of our JSON data. 9013ae8cdbSMarc-André Lureau nulpos = data.rfind(b'\0') 9113ae8cdbSMarc-André Lureau jsonpos = data.find(b'{', nulpos) 92b1742570SAlexander Graf 93b1742570SAlexander Graf # Check backwards from there and see whether we guessed right 94b1742570SAlexander Graf self.file.seek(datapos + jsonpos - 5, 0) 95b1742570SAlexander Graf if self.read8() != QEMU_VM_VMDESCRIPTION: 96b1742570SAlexander Graf raise Exception("No Debug Migration device found") 97b1742570SAlexander Graf 98b1742570SAlexander Graf jsonlen = self.read32() 99b1742570SAlexander Graf 100b1742570SAlexander Graf # Seek back to where we were at the beginning 101b1742570SAlexander Graf self.file.seek(entrypos, 0) 102b1742570SAlexander Graf 10314f9cec7SAlexey Kirillov # explicit decode() needed for Python 3.5 compatibility 10414f9cec7SAlexey Kirillov return data[jsonpos:jsonpos + jsonlen].decode("utf-8") 105b1742570SAlexander Graf 106b1742570SAlexander Graf def close(self): 107b1742570SAlexander Graf self.file.close() 108b1742570SAlexander Graf 109b1742570SAlexander Grafclass RamSection(object): 110b1742570SAlexander Graf RAM_SAVE_FLAG_COMPRESS = 0x02 111b1742570SAlexander Graf RAM_SAVE_FLAG_MEM_SIZE = 0x04 112b1742570SAlexander Graf RAM_SAVE_FLAG_PAGE = 0x08 113b1742570SAlexander Graf RAM_SAVE_FLAG_EOS = 0x10 114b1742570SAlexander Graf RAM_SAVE_FLAG_CONTINUE = 0x20 115b1742570SAlexander Graf RAM_SAVE_FLAG_XBZRLE = 0x40 116b1742570SAlexander Graf RAM_SAVE_FLAG_HOOK = 0x80 117f1de3097SMarc-André Lureau RAM_SAVE_FLAG_COMPRESS_PAGE = 0x100 118f1de3097SMarc-André Lureau RAM_SAVE_FLAG_MULTIFD_FLUSH = 0x200 119b1742570SAlexander Graf 120b1742570SAlexander Graf def __init__(self, file, version_id, ramargs, section_key): 121b1742570SAlexander Graf if version_id != 4: 122b1742570SAlexander Graf raise Exception("Unknown RAM version %d" % version_id) 123b1742570SAlexander Graf 124b1742570SAlexander Graf self.file = file 125b1742570SAlexander Graf self.section_key = section_key 126b1742570SAlexander Graf self.TARGET_PAGE_SIZE = ramargs['page_size'] 127b1742570SAlexander Graf self.dump_memory = ramargs['dump_memory'] 128b1742570SAlexander Graf self.write_memory = ramargs['write_memory'] 129ff40c7f0SFabiano Rosas self.ignore_shared = ramargs['ignore_shared'] 130b1742570SAlexander Graf self.sizeinfo = collections.OrderedDict() 131b1742570SAlexander Graf self.data = collections.OrderedDict() 132b1742570SAlexander Graf self.data['section sizes'] = self.sizeinfo 133b1742570SAlexander Graf self.name = '' 134b1742570SAlexander Graf if self.write_memory: 135b1742570SAlexander Graf self.files = { } 136b1742570SAlexander Graf if self.dump_memory: 137b1742570SAlexander Graf self.memory = collections.OrderedDict() 138b1742570SAlexander Graf self.data['memory'] = self.memory 139b1742570SAlexander Graf 140b1742570SAlexander Graf def __repr__(self): 141b1742570SAlexander Graf return self.data.__repr__() 142b1742570SAlexander Graf 143b1742570SAlexander Graf def __str__(self): 144b1742570SAlexander Graf return self.data.__str__() 145b1742570SAlexander Graf 146b1742570SAlexander Graf def getDict(self): 147b1742570SAlexander Graf return self.data 148b1742570SAlexander Graf 149b1742570SAlexander Graf def read(self): 150b1742570SAlexander Graf # Read all RAM sections 151b1742570SAlexander Graf while True: 152b1742570SAlexander Graf addr = self.file.read64() 153b1742570SAlexander Graf flags = addr & (self.TARGET_PAGE_SIZE - 1) 154b1742570SAlexander Graf addr &= ~(self.TARGET_PAGE_SIZE - 1) 155b1742570SAlexander Graf 156b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_MEM_SIZE: 157434b8adcSPeter Xu total_length = addr 158434b8adcSPeter Xu while total_length > 0: 159b1742570SAlexander Graf namelen = self.file.read8() 160b1742570SAlexander Graf self.name = self.file.readstr(len = namelen) 161b1742570SAlexander Graf len = self.file.read64() 162434b8adcSPeter Xu total_length -= len 163b1742570SAlexander Graf self.sizeinfo[self.name] = '0x%016x' % len 164b1742570SAlexander Graf if self.write_memory: 165f03868bdSEduardo Habkost print(self.name) 166b1742570SAlexander Graf mkdir_p('./' + os.path.dirname(self.name)) 167b1742570SAlexander Graf f = open('./' + self.name, "wb") 168b1742570SAlexander Graf f.truncate(0) 169b1742570SAlexander Graf f.truncate(len) 170b1742570SAlexander Graf self.files[self.name] = f 171ff40c7f0SFabiano Rosas if self.ignore_shared: 172ff40c7f0SFabiano Rosas mr_addr = self.file.read64() 173b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE 174b1742570SAlexander Graf 175b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_COMPRESS: 176b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_CONTINUE: 177b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_CONTINUE 178b1742570SAlexander Graf else: 179b1742570SAlexander Graf self.name = self.file.readstr() 180b1742570SAlexander Graf fill_char = self.file.read8() 181b1742570SAlexander Graf # The page in question is filled with fill_char now 182b1742570SAlexander Graf if self.write_memory and fill_char != 0: 183b1742570SAlexander Graf self.files[self.name].seek(addr, os.SEEK_SET) 184b1742570SAlexander Graf self.files[self.name].write(chr(fill_char) * self.TARGET_PAGE_SIZE) 185b1742570SAlexander Graf if self.dump_memory: 186b1742570SAlexander Graf self.memory['%s (0x%016x)' % (self.name, addr)] = 'Filled with 0x%02x' % fill_char 187b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_COMPRESS 188b1742570SAlexander Graf elif flags & self.RAM_SAVE_FLAG_PAGE: 189b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_CONTINUE: 190b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_CONTINUE 191b1742570SAlexander Graf else: 192b1742570SAlexander Graf self.name = self.file.readstr() 193b1742570SAlexander Graf 194b1742570SAlexander Graf if self.write_memory or self.dump_memory: 195b1742570SAlexander Graf data = self.file.readvar(size = self.TARGET_PAGE_SIZE) 196b1742570SAlexander Graf else: # Just skip RAM data 197b1742570SAlexander Graf self.file.file.seek(self.TARGET_PAGE_SIZE, 1) 198b1742570SAlexander Graf 199b1742570SAlexander Graf if self.write_memory: 200b1742570SAlexander Graf self.files[self.name].seek(addr, os.SEEK_SET) 201b1742570SAlexander Graf self.files[self.name].write(data) 202b1742570SAlexander Graf if self.dump_memory: 203b1742570SAlexander Graf hexdata = " ".join("{0:02x}".format(ord(c)) for c in data) 204b1742570SAlexander Graf self.memory['%s (0x%016x)' % (self.name, addr)] = hexdata 205b1742570SAlexander Graf 206b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_PAGE 207b1742570SAlexander Graf elif flags & self.RAM_SAVE_FLAG_XBZRLE: 208b1742570SAlexander Graf raise Exception("XBZRLE RAM compression is not supported yet") 209b1742570SAlexander Graf elif flags & self.RAM_SAVE_FLAG_HOOK: 210b1742570SAlexander Graf raise Exception("RAM hooks don't make sense with files") 211f1de3097SMarc-André Lureau if flags & self.RAM_SAVE_FLAG_MULTIFD_FLUSH: 212f1de3097SMarc-André Lureau continue 213b1742570SAlexander Graf 214b1742570SAlexander Graf # End of RAM section 215b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_EOS: 216b1742570SAlexander Graf break 217b1742570SAlexander Graf 218b1742570SAlexander Graf if flags != 0: 219b1742570SAlexander Graf raise Exception("Unknown RAM flags: %x" % flags) 220b1742570SAlexander Graf 221b1742570SAlexander Graf def __del__(self): 222b1742570SAlexander Graf if self.write_memory: 223b1742570SAlexander Graf for key in self.files: 224b1742570SAlexander Graf self.files[key].close() 225b1742570SAlexander Graf 226b1742570SAlexander Graf 227b1742570SAlexander Grafclass HTABSection(object): 228b1742570SAlexander Graf HASH_PTE_SIZE_64 = 16 229b1742570SAlexander Graf 230b1742570SAlexander Graf def __init__(self, file, version_id, device, section_key): 231b1742570SAlexander Graf if version_id != 1: 232b1742570SAlexander Graf raise Exception("Unknown HTAB version %d" % version_id) 233b1742570SAlexander Graf 234b1742570SAlexander Graf self.file = file 235b1742570SAlexander Graf self.section_key = section_key 236b1742570SAlexander Graf 237b1742570SAlexander Graf def read(self): 238b1742570SAlexander Graf 239b1742570SAlexander Graf header = self.file.read32() 240b1742570SAlexander Graf 241029ff892SLaurent Vivier if (header == -1): 242029ff892SLaurent Vivier # "no HPT" encoding 243029ff892SLaurent Vivier return 244029ff892SLaurent Vivier 245b1742570SAlexander Graf if (header > 0): 246b1742570SAlexander Graf # First section, just the hash shift 247b1742570SAlexander Graf return 248b1742570SAlexander Graf 249b1742570SAlexander Graf # Read until end marker 250b1742570SAlexander Graf while True: 251b1742570SAlexander Graf index = self.file.read32() 252b1742570SAlexander Graf n_valid = self.file.read16() 253b1742570SAlexander Graf n_invalid = self.file.read16() 254b1742570SAlexander Graf 255b1742570SAlexander Graf if index == 0 and n_valid == 0 and n_invalid == 0: 256b1742570SAlexander Graf break 257b1742570SAlexander Graf 258be7433efSGreg Kurz self.file.readvar(n_valid * self.HASH_PTE_SIZE_64) 259b1742570SAlexander Graf 260b1742570SAlexander Graf def getDict(self): 261b1742570SAlexander Graf return "" 262b1742570SAlexander Graf 26396e5c9bcSMark Cave-Ayland 26481c2c9ddSThomas Huthclass S390StorageAttributes(object): 26581c2c9ddSThomas Huth STATTR_FLAG_EOS = 0x01 26681c2c9ddSThomas Huth STATTR_FLAG_MORE = 0x02 26781c2c9ddSThomas Huth STATTR_FLAG_ERROR = 0x04 26881c2c9ddSThomas Huth STATTR_FLAG_DONE = 0x08 26981c2c9ddSThomas Huth 27081c2c9ddSThomas Huth def __init__(self, file, version_id, device, section_key): 27181c2c9ddSThomas Huth if version_id != 0: 27281c2c9ddSThomas Huth raise Exception("Unknown storage_attributes version %d" % version_id) 27381c2c9ddSThomas Huth 27481c2c9ddSThomas Huth self.file = file 27581c2c9ddSThomas Huth self.section_key = section_key 27681c2c9ddSThomas Huth 27781c2c9ddSThomas Huth def read(self): 278e3839b0cSFabiano Rosas pos = 0 27981c2c9ddSThomas Huth while True: 28081c2c9ddSThomas Huth addr_flags = self.file.read64() 28181c2c9ddSThomas Huth flags = addr_flags & 0xfff 282e3839b0cSFabiano Rosas 283e3839b0cSFabiano Rosas if flags & self.STATTR_FLAG_DONE: 284e3839b0cSFabiano Rosas pos = self.file.tell() 285e3839b0cSFabiano Rosas continue 286e3839b0cSFabiano Rosas elif flags & self.STATTR_FLAG_EOS: 28781c2c9ddSThomas Huth return 288e3839b0cSFabiano Rosas else: 289e3839b0cSFabiano Rosas # No EOS came after DONE, that's OK, but rewind the 290e3839b0cSFabiano Rosas # stream because this is not our data. 291e3839b0cSFabiano Rosas if pos: 292e3839b0cSFabiano Rosas self.file.seek(pos, os.SEEK_SET) 293e3839b0cSFabiano Rosas return 294e3839b0cSFabiano Rosas raise Exception("Unknown flags %x", flags) 295e3839b0cSFabiano Rosas 29681c2c9ddSThomas Huth if (flags & self.STATTR_FLAG_ERROR): 29781c2c9ddSThomas Huth raise Exception("Error in migration stream") 29881c2c9ddSThomas Huth count = self.file.read64() 29981c2c9ddSThomas Huth self.file.readvar(count) 30081c2c9ddSThomas Huth 30181c2c9ddSThomas Huth def getDict(self): 30281c2c9ddSThomas Huth return "" 30381c2c9ddSThomas Huth 30481c2c9ddSThomas Huth 30596e5c9bcSMark Cave-Aylandclass ConfigurationSection(object): 306c36c31c8SFabiano Rosas def __init__(self, file, desc): 30796e5c9bcSMark Cave-Ayland self.file = file 308c36c31c8SFabiano Rosas self.desc = desc 30931499a9dSFabiano Rosas self.caps = [] 31031499a9dSFabiano Rosas 31131499a9dSFabiano Rosas def parse_capabilities(self, vmsd_caps): 31231499a9dSFabiano Rosas if not vmsd_caps: 31331499a9dSFabiano Rosas return 31431499a9dSFabiano Rosas 31531499a9dSFabiano Rosas ncaps = vmsd_caps.data['caps_count'].data 31631499a9dSFabiano Rosas self.caps = vmsd_caps.data['capabilities'] 31731499a9dSFabiano Rosas 31831499a9dSFabiano Rosas if type(self.caps) != list: 31931499a9dSFabiano Rosas self.caps = [self.caps] 32031499a9dSFabiano Rosas 32131499a9dSFabiano Rosas if len(self.caps) != ncaps: 32231499a9dSFabiano Rosas raise Exception("Number of capabilities doesn't match " 32331499a9dSFabiano Rosas "caps_count field") 32431499a9dSFabiano Rosas 32531499a9dSFabiano Rosas def has_capability(self, cap): 32631499a9dSFabiano Rosas return any([str(c) == cap for c in self.caps]) 32796e5c9bcSMark Cave-Ayland 32896e5c9bcSMark Cave-Ayland def read(self): 329c36c31c8SFabiano Rosas if self.desc: 330c36c31c8SFabiano Rosas version_id = self.desc['version'] 331c36c31c8SFabiano Rosas section = VMSDSection(self.file, version_id, self.desc, 332c36c31c8SFabiano Rosas 'configuration') 333c36c31c8SFabiano Rosas section.read() 33431499a9dSFabiano Rosas self.parse_capabilities( 33531499a9dSFabiano Rosas section.data.get("configuration/capabilities")) 336c36c31c8SFabiano Rosas else: 337c36c31c8SFabiano Rosas # backward compatibility for older streams that don't have 338c36c31c8SFabiano Rosas # the configuration section in the json 33996e5c9bcSMark Cave-Ayland name_len = self.file.read32() 34096e5c9bcSMark Cave-Ayland name = self.file.readstr(len = name_len) 34196e5c9bcSMark Cave-Ayland 342b1742570SAlexander Grafclass VMSDFieldGeneric(object): 343b1742570SAlexander Graf def __init__(self, desc, file): 344b1742570SAlexander Graf self.file = file 345b1742570SAlexander Graf self.desc = desc 346b1742570SAlexander Graf self.data = "" 347b1742570SAlexander Graf 348b1742570SAlexander Graf def __repr__(self): 349b1742570SAlexander Graf return str(self.__str__()) 350b1742570SAlexander Graf 351b1742570SAlexander Graf def __str__(self): 35283de0ea4SMarc-André Lureau return " ".join("{0:02x}".format(c) for c in self.data) 353b1742570SAlexander Graf 354b1742570SAlexander Graf def getDict(self): 355b1742570SAlexander Graf return self.__str__() 356b1742570SAlexander Graf 357b1742570SAlexander Graf def read(self): 358b1742570SAlexander Graf size = int(self.desc['size']) 359b1742570SAlexander Graf self.data = self.file.readvar(size) 360b1742570SAlexander Graf return self.data 361b1742570SAlexander Graf 36231499a9dSFabiano Rosasclass VMSDFieldCap(object): 36331499a9dSFabiano Rosas def __init__(self, desc, file): 36431499a9dSFabiano Rosas self.file = file 36531499a9dSFabiano Rosas self.desc = desc 36631499a9dSFabiano Rosas self.data = "" 36731499a9dSFabiano Rosas 36831499a9dSFabiano Rosas def __repr__(self): 36931499a9dSFabiano Rosas return self.data 37031499a9dSFabiano Rosas 37131499a9dSFabiano Rosas def __str__(self): 37231499a9dSFabiano Rosas return self.data 37331499a9dSFabiano Rosas 37431499a9dSFabiano Rosas def read(self): 37531499a9dSFabiano Rosas len = self.file.read8() 37631499a9dSFabiano Rosas self.data = self.file.readstr(len) 37731499a9dSFabiano Rosas 37831499a9dSFabiano Rosas 379b1742570SAlexander Grafclass VMSDFieldInt(VMSDFieldGeneric): 380b1742570SAlexander Graf def __init__(self, desc, file): 381b1742570SAlexander Graf super(VMSDFieldInt, self).__init__(desc, file) 382b1742570SAlexander Graf self.size = int(desc['size']) 383b1742570SAlexander Graf self.format = '0x%%0%dx' % (self.size * 2) 384b1742570SAlexander Graf self.sdtype = '>i%d' % self.size 385b1742570SAlexander Graf self.udtype = '>u%d' % self.size 386b1742570SAlexander Graf 387b1742570SAlexander Graf def __repr__(self): 388b1742570SAlexander Graf if self.data < 0: 389b1742570SAlexander Graf return ('%s (%d)' % ((self.format % self.udata), self.data)) 390b1742570SAlexander Graf else: 391b1742570SAlexander Graf return self.format % self.data 392b1742570SAlexander Graf 393b1742570SAlexander Graf def __str__(self): 394b1742570SAlexander Graf return self.__repr__() 395b1742570SAlexander Graf 396b1742570SAlexander Graf def getDict(self): 397b1742570SAlexander Graf return self.__str__() 398b1742570SAlexander Graf 399b1742570SAlexander Graf def read(self): 400b1742570SAlexander Graf super(VMSDFieldInt, self).read() 40183de0ea4SMarc-André Lureau self.sdata = int.from_bytes(self.data, byteorder='big', signed=True) 40283de0ea4SMarc-André Lureau self.udata = int.from_bytes(self.data, byteorder='big', signed=False) 403b1742570SAlexander Graf self.data = self.sdata 404b1742570SAlexander Graf return self.data 405b1742570SAlexander Graf 406b1742570SAlexander Grafclass VMSDFieldUInt(VMSDFieldInt): 407b1742570SAlexander Graf def __init__(self, desc, file): 408b1742570SAlexander Graf super(VMSDFieldUInt, self).__init__(desc, file) 409b1742570SAlexander Graf 410b1742570SAlexander Graf def read(self): 411b1742570SAlexander Graf super(VMSDFieldUInt, self).read() 412b1742570SAlexander Graf self.data = self.udata 413b1742570SAlexander Graf return self.data 414b1742570SAlexander Graf 415b1742570SAlexander Grafclass VMSDFieldIntLE(VMSDFieldInt): 416b1742570SAlexander Graf def __init__(self, desc, file): 417b1742570SAlexander Graf super(VMSDFieldIntLE, self).__init__(desc, file) 418b1742570SAlexander Graf self.dtype = '<i%d' % self.size 419b1742570SAlexander Graf 4203ba6e116SFabiano Rosasclass VMSDFieldNull(VMSDFieldGeneric): 4213ba6e116SFabiano Rosas NULL_PTR_MARKER = b'0' 4223ba6e116SFabiano Rosas 4233ba6e116SFabiano Rosas def __init__(self, desc, file): 4243ba6e116SFabiano Rosas super(VMSDFieldNull, self).__init__(desc, file) 4253ba6e116SFabiano Rosas 4263ba6e116SFabiano Rosas def __repr__(self): 4273ba6e116SFabiano Rosas # A NULL pointer is encoded in the stream as a '0' to 4283ba6e116SFabiano Rosas # disambiguate from a mere 0x0 value and avoid consumers 4293ba6e116SFabiano Rosas # trying to follow the NULL pointer. Displaying '0', 0x30 or 4303ba6e116SFabiano Rosas # 0x0 when analyzing the JSON debug stream could become 4313ba6e116SFabiano Rosas # confusing, so use an explicit term instead. 4323ba6e116SFabiano Rosas return "nullptr" 4333ba6e116SFabiano Rosas 4343ba6e116SFabiano Rosas def __str__(self): 4353ba6e116SFabiano Rosas return self.__repr__() 4363ba6e116SFabiano Rosas 4373ba6e116SFabiano Rosas def read(self): 4383ba6e116SFabiano Rosas super(VMSDFieldNull, self).read() 4393ba6e116SFabiano Rosas assert(self.data == self.NULL_PTR_MARKER) 4403ba6e116SFabiano Rosas return self.data 4413ba6e116SFabiano Rosas 442b1742570SAlexander Grafclass VMSDFieldBool(VMSDFieldGeneric): 443b1742570SAlexander Graf def __init__(self, desc, file): 444b1742570SAlexander Graf super(VMSDFieldBool, self).__init__(desc, file) 445b1742570SAlexander Graf 446b1742570SAlexander Graf def __repr__(self): 447b1742570SAlexander Graf return self.data.__repr__() 448b1742570SAlexander Graf 449b1742570SAlexander Graf def __str__(self): 450b1742570SAlexander Graf return self.data.__str__() 451b1742570SAlexander Graf 452b1742570SAlexander Graf def getDict(self): 453b1742570SAlexander Graf return self.data 454b1742570SAlexander Graf 455b1742570SAlexander Graf def read(self): 456b1742570SAlexander Graf super(VMSDFieldBool, self).read() 457b1742570SAlexander Graf if self.data[0] == 0: 458b1742570SAlexander Graf self.data = False 459b1742570SAlexander Graf else: 460b1742570SAlexander Graf self.data = True 461b1742570SAlexander Graf return self.data 462b1742570SAlexander Graf 463b1742570SAlexander Grafclass VMSDFieldStruct(VMSDFieldGeneric): 464b1742570SAlexander Graf QEMU_VM_SUBSECTION = 0x05 465b1742570SAlexander Graf 466b1742570SAlexander Graf def __init__(self, desc, file): 467b1742570SAlexander Graf super(VMSDFieldStruct, self).__init__(desc, file) 468b1742570SAlexander Graf self.data = collections.OrderedDict() 469b1742570SAlexander Graf 470ea3b8215SFabiano Rosas if 'fields' not in self.desc['struct']: 471ea3b8215SFabiano Rosas raise Exception("No fields in struct. VMSD:\n%s" % self.desc) 472ea3b8215SFabiano Rosas 473b1742570SAlexander Graf # When we see compressed array elements, unfold them here 474b1742570SAlexander Graf new_fields = [] 475b1742570SAlexander Graf for field in self.desc['struct']['fields']: 476b1742570SAlexander Graf if not 'array_len' in field: 477b1742570SAlexander Graf new_fields.append(field) 478b1742570SAlexander Graf continue 479b1742570SAlexander Graf array_len = field.pop('array_len') 480b1742570SAlexander Graf field['index'] = 0 481b1742570SAlexander Graf new_fields.append(field) 48283de0ea4SMarc-André Lureau for i in range(1, array_len): 483b1742570SAlexander Graf c = field.copy() 484b1742570SAlexander Graf c['index'] = i 485b1742570SAlexander Graf new_fields.append(c) 486b1742570SAlexander Graf 487b1742570SAlexander Graf self.desc['struct']['fields'] = new_fields 488b1742570SAlexander Graf 489b1742570SAlexander Graf def __repr__(self): 490b1742570SAlexander Graf return self.data.__repr__() 491b1742570SAlexander Graf 492b1742570SAlexander Graf def __str__(self): 493b1742570SAlexander Graf return self.data.__str__() 494b1742570SAlexander Graf 495b1742570SAlexander Graf def read(self): 496b1742570SAlexander Graf for field in self.desc['struct']['fields']: 497b1742570SAlexander Graf try: 498b1742570SAlexander Graf reader = vmsd_field_readers[field['type']] 499b1742570SAlexander Graf except: 500b1742570SAlexander Graf reader = VMSDFieldGeneric 501b1742570SAlexander Graf 502b1742570SAlexander Graf field['data'] = reader(field, self.file) 503b1742570SAlexander Graf field['data'].read() 504b1742570SAlexander Graf 505*82565fb6SFabiano Rosas fname = field['name'] 506*82565fb6SFabiano Rosas fdata = field['data'] 507*82565fb6SFabiano Rosas 508*82565fb6SFabiano Rosas # The field could be: 509*82565fb6SFabiano Rosas # i) a single data entry, e.g. uint64 510*82565fb6SFabiano Rosas # ii) an array, indicated by it containing the 'index' key 511*82565fb6SFabiano Rosas # 512*82565fb6SFabiano Rosas # However, the overall data after parsing the whole 513*82565fb6SFabiano Rosas # stream, could be a mix of arrays and single data fields, 514*82565fb6SFabiano Rosas # all sharing the same field name due to how QEMU breaks 515*82565fb6SFabiano Rosas # up arrays with NULL pointers into multiple compressed 516*82565fb6SFabiano Rosas # array segments. 517*82565fb6SFabiano Rosas if fname not in self.data: 518*82565fb6SFabiano Rosas self.data[fname] = fdata 519*82565fb6SFabiano Rosas elif type(self.data[fname]) == list: 520*82565fb6SFabiano Rosas self.data[fname].append(fdata) 521b1742570SAlexander Graf else: 522*82565fb6SFabiano Rosas tmp = self.data[fname] 523*82565fb6SFabiano Rosas self.data[fname] = [tmp, fdata] 524b1742570SAlexander Graf 525b1742570SAlexander Graf if 'subsections' in self.desc['struct']: 526b1742570SAlexander Graf for subsection in self.desc['struct']['subsections']: 527b1742570SAlexander Graf if self.file.read8() != self.QEMU_VM_SUBSECTION: 528b1742570SAlexander Graf raise Exception("Subsection %s not found at offset %x" % ( subsection['vmsd_name'], self.file.tell())) 529b1742570SAlexander Graf name = self.file.readstr() 530b1742570SAlexander Graf version_id = self.file.read32() 531ea3b8215SFabiano Rosas 532ea3b8215SFabiano Rosas if not subsection: 533ea3b8215SFabiano Rosas raise Exception("Empty description for subsection: %s" % name) 534ea3b8215SFabiano Rosas 535b1742570SAlexander Graf self.data[name] = VMSDSection(self.file, version_id, subsection, (name, 0)) 536b1742570SAlexander Graf self.data[name].read() 537b1742570SAlexander Graf 538b1742570SAlexander Graf def getDictItem(self, value): 539b1742570SAlexander Graf # Strings would fall into the array category, treat 540b1742570SAlexander Graf # them specially 541b1742570SAlexander Graf if value.__class__ is ''.__class__: 542b1742570SAlexander Graf return value 543b1742570SAlexander Graf 544b1742570SAlexander Graf try: 545b1742570SAlexander Graf return self.getDictOrderedDict(value) 546b1742570SAlexander Graf except: 547b1742570SAlexander Graf try: 548b1742570SAlexander Graf return self.getDictArray(value) 549b1742570SAlexander Graf except: 550b1742570SAlexander Graf try: 551b1742570SAlexander Graf return value.getDict() 552b1742570SAlexander Graf except: 553b1742570SAlexander Graf return value 554b1742570SAlexander Graf 555b1742570SAlexander Graf def getDictArray(self, array): 556b1742570SAlexander Graf r = [] 557b1742570SAlexander Graf for value in array: 558b1742570SAlexander Graf r.append(self.getDictItem(value)) 559b1742570SAlexander Graf return r 560b1742570SAlexander Graf 561b1742570SAlexander Graf def getDictOrderedDict(self, dict): 562b1742570SAlexander Graf r = collections.OrderedDict() 563b1742570SAlexander Graf for (key, value) in dict.items(): 564b1742570SAlexander Graf r[key] = self.getDictItem(value) 565b1742570SAlexander Graf return r 566b1742570SAlexander Graf 567b1742570SAlexander Graf def getDict(self): 568b1742570SAlexander Graf return self.getDictOrderedDict(self.data) 569b1742570SAlexander Graf 570b1742570SAlexander Grafvmsd_field_readers = { 571b1742570SAlexander Graf "bool" : VMSDFieldBool, 572b1742570SAlexander Graf "int8" : VMSDFieldInt, 573b1742570SAlexander Graf "int16" : VMSDFieldInt, 574b1742570SAlexander Graf "int32" : VMSDFieldInt, 575b1742570SAlexander Graf "int32 equal" : VMSDFieldInt, 576b1742570SAlexander Graf "int32 le" : VMSDFieldIntLE, 577b1742570SAlexander Graf "int64" : VMSDFieldInt, 578b1742570SAlexander Graf "uint8" : VMSDFieldUInt, 579b1742570SAlexander Graf "uint16" : VMSDFieldUInt, 580b1742570SAlexander Graf "uint32" : VMSDFieldUInt, 581b1742570SAlexander Graf "uint32 equal" : VMSDFieldUInt, 582b1742570SAlexander Graf "uint64" : VMSDFieldUInt, 583b1742570SAlexander Graf "int64 equal" : VMSDFieldInt, 584b1742570SAlexander Graf "uint8 equal" : VMSDFieldInt, 585b1742570SAlexander Graf "uint16 equal" : VMSDFieldInt, 586b1742570SAlexander Graf "float64" : VMSDFieldGeneric, 587b1742570SAlexander Graf "timer" : VMSDFieldGeneric, 588b1742570SAlexander Graf "buffer" : VMSDFieldGeneric, 589b1742570SAlexander Graf "unused_buffer" : VMSDFieldGeneric, 590b1742570SAlexander Graf "bitmap" : VMSDFieldGeneric, 591b1742570SAlexander Graf "struct" : VMSDFieldStruct, 59231499a9dSFabiano Rosas "capability": VMSDFieldCap, 5933ba6e116SFabiano Rosas "nullptr": VMSDFieldNull, 594b1742570SAlexander Graf "unknown" : VMSDFieldGeneric, 595b1742570SAlexander Graf} 596b1742570SAlexander Graf 597b1742570SAlexander Grafclass VMSDSection(VMSDFieldStruct): 598b1742570SAlexander Graf def __init__(self, file, version_id, device, section_key): 599b1742570SAlexander Graf self.file = file 600b1742570SAlexander Graf self.data = "" 601b1742570SAlexander Graf self.vmsd_name = "" 602b1742570SAlexander Graf self.section_key = section_key 603b1742570SAlexander Graf desc = device 604b1742570SAlexander Graf if 'vmsd_name' in device: 605b1742570SAlexander Graf self.vmsd_name = device['vmsd_name'] 606b1742570SAlexander Graf 607b1742570SAlexander Graf # A section really is nothing but a FieldStruct :) 608b1742570SAlexander Graf super(VMSDSection, self).__init__({ 'struct' : desc }, file) 609b1742570SAlexander Graf 610b1742570SAlexander Graf############################################################################### 611b1742570SAlexander Graf 612b1742570SAlexander Grafclass MigrationDump(object): 613b1742570SAlexander Graf QEMU_VM_FILE_MAGIC = 0x5145564d 614b1742570SAlexander Graf QEMU_VM_FILE_VERSION = 0x00000003 615b1742570SAlexander Graf QEMU_VM_EOF = 0x00 616b1742570SAlexander Graf QEMU_VM_SECTION_START = 0x01 617b1742570SAlexander Graf QEMU_VM_SECTION_PART = 0x02 618b1742570SAlexander Graf QEMU_VM_SECTION_END = 0x03 619b1742570SAlexander Graf QEMU_VM_SECTION_FULL = 0x04 620b1742570SAlexander Graf QEMU_VM_SUBSECTION = 0x05 621b1742570SAlexander Graf QEMU_VM_VMDESCRIPTION = 0x06 62296e5c9bcSMark Cave-Ayland QEMU_VM_CONFIGURATION = 0x07 62373d9a796SDr. David Alan Gilbert QEMU_VM_SECTION_FOOTER= 0x7e 624b1742570SAlexander Graf 625b1742570SAlexander Graf def __init__(self, filename): 62681c2c9ddSThomas Huth self.section_classes = { 62781c2c9ddSThomas Huth ( 'ram', 0 ) : [ RamSection, None ], 62881c2c9ddSThomas Huth ( 's390-storage_attributes', 0 ) : [ S390StorageAttributes, None], 62981c2c9ddSThomas Huth ( 'spapr/htab', 0) : ( HTABSection, None ) 63081c2c9ddSThomas Huth } 631b1742570SAlexander Graf self.filename = filename 632b1742570SAlexander Graf self.vmsd_desc = None 633ea3b8215SFabiano Rosas self.vmsd_json = "" 634b1742570SAlexander Graf 635ea3b8215SFabiano Rosas def read(self, desc_only = False, dump_memory = False, 636ea3b8215SFabiano Rosas write_memory = False): 637b1742570SAlexander Graf # Read in the whole file 638b1742570SAlexander Graf file = MigrationFile(self.filename) 639ea3b8215SFabiano Rosas self.vmsd_json = file.read_migration_debug_json() 640b1742570SAlexander Graf 641b1742570SAlexander Graf # File magic 642b1742570SAlexander Graf data = file.read32() 643b1742570SAlexander Graf if data != self.QEMU_VM_FILE_MAGIC: 644b1742570SAlexander Graf raise Exception("Invalid file magic %x" % data) 645b1742570SAlexander Graf 646b1742570SAlexander Graf # Version (has to be v3) 647b1742570SAlexander Graf data = file.read32() 648b1742570SAlexander Graf if data != self.QEMU_VM_FILE_VERSION: 649b1742570SAlexander Graf raise Exception("Invalid version number %d" % data) 650b1742570SAlexander Graf 651b1742570SAlexander Graf self.load_vmsd_json(file) 652b1742570SAlexander Graf 653b1742570SAlexander Graf # Read sections 654b1742570SAlexander Graf self.sections = collections.OrderedDict() 655b1742570SAlexander Graf 656b1742570SAlexander Graf if desc_only: 657b1742570SAlexander Graf return 658b1742570SAlexander Graf 659b1742570SAlexander Graf ramargs = {} 660b1742570SAlexander Graf ramargs['page_size'] = self.vmsd_desc['page_size'] 661b1742570SAlexander Graf ramargs['dump_memory'] = dump_memory 662b1742570SAlexander Graf ramargs['write_memory'] = write_memory 663ff40c7f0SFabiano Rosas ramargs['ignore_shared'] = False 664b1742570SAlexander Graf self.section_classes[('ram',0)][1] = ramargs 665b1742570SAlexander Graf 666b1742570SAlexander Graf while True: 667b1742570SAlexander Graf section_type = file.read8() 668b1742570SAlexander Graf if section_type == self.QEMU_VM_EOF: 669b1742570SAlexander Graf break 67096e5c9bcSMark Cave-Ayland elif section_type == self.QEMU_VM_CONFIGURATION: 671c36c31c8SFabiano Rosas config_desc = self.vmsd_desc.get('configuration') 672c36c31c8SFabiano Rosas section = ConfigurationSection(file, config_desc) 67396e5c9bcSMark Cave-Ayland section.read() 674ff40c7f0SFabiano Rosas ramargs['ignore_shared'] = section.has_capability('x-ignore-shared') 675b1742570SAlexander Graf elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL: 676b1742570SAlexander Graf section_id = file.read32() 677b1742570SAlexander Graf name = file.readstr() 678b1742570SAlexander Graf instance_id = file.read32() 679b1742570SAlexander Graf version_id = file.read32() 680b1742570SAlexander Graf section_key = (name, instance_id) 681b1742570SAlexander Graf classdesc = self.section_classes[section_key] 682b1742570SAlexander Graf section = classdesc[0](file, version_id, classdesc[1], section_key) 683b1742570SAlexander Graf self.sections[section_id] = section 684b1742570SAlexander Graf section.read() 685b1742570SAlexander Graf elif section_type == self.QEMU_VM_SECTION_PART or section_type == self.QEMU_VM_SECTION_END: 686b1742570SAlexander Graf section_id = file.read32() 687b1742570SAlexander Graf self.sections[section_id].read() 68873d9a796SDr. David Alan Gilbert elif section_type == self.QEMU_VM_SECTION_FOOTER: 68973d9a796SDr. David Alan Gilbert read_section_id = file.read32() 69073d9a796SDr. David Alan Gilbert if read_section_id != section_id: 69173d9a796SDr. David Alan Gilbert raise Exception("Mismatched section footer: %x vs %x" % (read_section_id, section_id)) 692b1742570SAlexander Graf else: 693b1742570SAlexander Graf raise Exception("Unknown section type: %d" % section_type) 694b1742570SAlexander Graf file.close() 695b1742570SAlexander Graf 696b1742570SAlexander Graf def load_vmsd_json(self, file): 697ea3b8215SFabiano Rosas self.vmsd_desc = json.loads(self.vmsd_json, 698ea3b8215SFabiano Rosas object_pairs_hook=collections.OrderedDict) 699b1742570SAlexander Graf for device in self.vmsd_desc['devices']: 700ea3b8215SFabiano Rosas if 'fields' not in device: 701ea3b8215SFabiano Rosas raise Exception("vmstate for device %s has no fields" % device['name']) 702b1742570SAlexander Graf key = (device['name'], device['instance_id']) 703b1742570SAlexander Graf value = ( VMSDSection, device ) 704b1742570SAlexander Graf self.section_classes[key] = value 705b1742570SAlexander Graf 706b1742570SAlexander Graf def getDict(self): 707b1742570SAlexander Graf r = collections.OrderedDict() 708b1742570SAlexander Graf for (key, value) in self.sections.items(): 709b1742570SAlexander Graf key = "%s (%d)" % ( value.section_key[0], key ) 710b1742570SAlexander Graf r[key] = value.getDict() 711b1742570SAlexander Graf return r 712b1742570SAlexander Graf 713b1742570SAlexander Graf############################################################################### 714b1742570SAlexander Graf 715b1742570SAlexander Grafclass JSONEncoder(json.JSONEncoder): 716b1742570SAlexander Graf def default(self, o): 717b1742570SAlexander Graf if isinstance(o, VMSDFieldGeneric): 718b1742570SAlexander Graf return str(o) 719b1742570SAlexander Graf return json.JSONEncoder.default(self, o) 720b1742570SAlexander Graf 721b1742570SAlexander Grafparser = argparse.ArgumentParser() 722b1742570SAlexander Grafparser.add_argument("-f", "--file", help='migration dump to read from', required=True) 723b1742570SAlexander Grafparser.add_argument("-m", "--memory", help='dump RAM contents as well', action='store_true') 724b1742570SAlexander Grafparser.add_argument("-d", "--dump", help='what to dump ("state" or "desc")', default='state') 725b1742570SAlexander Grafparser.add_argument("-x", "--extract", help='extract contents into individual files', action='store_true') 726b1742570SAlexander Grafargs = parser.parse_args() 727b1742570SAlexander Graf 728b1742570SAlexander Grafjsonenc = JSONEncoder(indent=4, separators=(',', ': ')) 729b1742570SAlexander Graf 730ea3b8215SFabiano Rosasif not any([args.extract, args.dump == "state", args.dump == "desc"]): 731ea3b8215SFabiano Rosas raise Exception("Please specify either -x, -d state or -d desc") 732ea3b8215SFabiano Rosas 733ea3b8215SFabiano Rosastry: 734b1742570SAlexander Graf dump = MigrationDump(args.file) 735b1742570SAlexander Graf 736ea3b8215SFabiano Rosas if args.extract: 737b1742570SAlexander Graf dump.read(desc_only = True) 738ea3b8215SFabiano Rosas 739f03868bdSEduardo Habkost print("desc.json") 7402c92be50SLaurent Vivier f = open("desc.json", "w") 741b1742570SAlexander Graf f.truncate() 742b1742570SAlexander Graf f.write(jsonenc.encode(dump.vmsd_desc)) 743b1742570SAlexander Graf f.close() 744b1742570SAlexander Graf 745b1742570SAlexander Graf dump.read(write_memory = True) 746b1742570SAlexander Graf dict = dump.getDict() 747f03868bdSEduardo Habkost print("state.json") 7482c92be50SLaurent Vivier f = open("state.json", "w") 749b1742570SAlexander Graf f.truncate() 750b1742570SAlexander Graf f.write(jsonenc.encode(dict)) 751b1742570SAlexander Graf f.close() 752b1742570SAlexander Graf elif args.dump == "state": 753b1742570SAlexander Graf dump.read(dump_memory = args.memory) 754b1742570SAlexander Graf dict = dump.getDict() 755f03868bdSEduardo Habkost print(jsonenc.encode(dict)) 756b1742570SAlexander Graf elif args.dump == "desc": 757b1742570SAlexander Graf dump.read(desc_only = True) 758f03868bdSEduardo Habkost print(jsonenc.encode(dump.vmsd_desc)) 759ea3b8215SFabiano Rosasexcept Exception: 760ea3b8215SFabiano Rosas raise Exception("Full JSON dump:\n%s", dump.vmsd_json) 761