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 68b1742570SAlexander Graf # The VMSD description is at the end of the file, after EOF. Look for 69b1742570SAlexander Graf # the last NULL byte, then for the beginning brace of JSON. 70b1742570SAlexander Graf def read_migration_debug_json(self): 71b1742570SAlexander Graf QEMU_VM_VMDESCRIPTION = 0x06 72b1742570SAlexander Graf 73b1742570SAlexander Graf # Remember the offset in the file when we started 74b1742570SAlexander Graf entrypos = self.file.tell() 75b1742570SAlexander Graf 76b1742570SAlexander Graf # Read the last 10MB 77b1742570SAlexander Graf self.file.seek(0, os.SEEK_END) 78b1742570SAlexander Graf endpos = self.file.tell() 79b1742570SAlexander Graf self.file.seek(max(-endpos, -10 * 1024 * 1024), os.SEEK_END) 80b1742570SAlexander Graf datapos = self.file.tell() 81b1742570SAlexander Graf data = self.file.read() 82b1742570SAlexander Graf # The full file read closed the file as well, reopen it 83b1742570SAlexander Graf self.file = open(self.filename, "rb") 84b1742570SAlexander Graf 85b1742570SAlexander Graf # Find the last NULL byte, then the first brace after that. This should 86b1742570SAlexander Graf # be the beginning of our JSON data. 8713ae8cdbSMarc-André Lureau nulpos = data.rfind(b'\0') 8813ae8cdbSMarc-André Lureau jsonpos = data.find(b'{', nulpos) 89b1742570SAlexander Graf 90b1742570SAlexander Graf # Check backwards from there and see whether we guessed right 91b1742570SAlexander Graf self.file.seek(datapos + jsonpos - 5, 0) 92b1742570SAlexander Graf if self.read8() != QEMU_VM_VMDESCRIPTION: 93b1742570SAlexander Graf raise Exception("No Debug Migration device found") 94b1742570SAlexander Graf 95b1742570SAlexander Graf jsonlen = self.read32() 96b1742570SAlexander Graf 97b1742570SAlexander Graf # Seek back to where we were at the beginning 98b1742570SAlexander Graf self.file.seek(entrypos, 0) 99b1742570SAlexander Graf 10014f9cec7SAlexey Kirillov # explicit decode() needed for Python 3.5 compatibility 10114f9cec7SAlexey Kirillov return data[jsonpos:jsonpos + jsonlen].decode("utf-8") 102b1742570SAlexander Graf 103b1742570SAlexander Graf def close(self): 104b1742570SAlexander Graf self.file.close() 105b1742570SAlexander Graf 106b1742570SAlexander Grafclass RamSection(object): 107b1742570SAlexander Graf RAM_SAVE_FLAG_COMPRESS = 0x02 108b1742570SAlexander Graf RAM_SAVE_FLAG_MEM_SIZE = 0x04 109b1742570SAlexander Graf RAM_SAVE_FLAG_PAGE = 0x08 110b1742570SAlexander Graf RAM_SAVE_FLAG_EOS = 0x10 111b1742570SAlexander Graf RAM_SAVE_FLAG_CONTINUE = 0x20 112b1742570SAlexander Graf RAM_SAVE_FLAG_XBZRLE = 0x40 113b1742570SAlexander Graf RAM_SAVE_FLAG_HOOK = 0x80 114f1de3097SMarc-André Lureau RAM_SAVE_FLAG_COMPRESS_PAGE = 0x100 115f1de3097SMarc-André Lureau RAM_SAVE_FLAG_MULTIFD_FLUSH = 0x200 116b1742570SAlexander Graf 117b1742570SAlexander Graf def __init__(self, file, version_id, ramargs, section_key): 118b1742570SAlexander Graf if version_id != 4: 119b1742570SAlexander Graf raise Exception("Unknown RAM version %d" % version_id) 120b1742570SAlexander Graf 121b1742570SAlexander Graf self.file = file 122b1742570SAlexander Graf self.section_key = section_key 123b1742570SAlexander Graf self.TARGET_PAGE_SIZE = ramargs['page_size'] 124b1742570SAlexander Graf self.dump_memory = ramargs['dump_memory'] 125b1742570SAlexander Graf self.write_memory = ramargs['write_memory'] 126ff40c7f0SFabiano Rosas self.ignore_shared = ramargs['ignore_shared'] 127b1742570SAlexander Graf self.sizeinfo = collections.OrderedDict() 128b1742570SAlexander Graf self.data = collections.OrderedDict() 129b1742570SAlexander Graf self.data['section sizes'] = self.sizeinfo 130b1742570SAlexander Graf self.name = '' 131b1742570SAlexander Graf if self.write_memory: 132b1742570SAlexander Graf self.files = { } 133b1742570SAlexander Graf if self.dump_memory: 134b1742570SAlexander Graf self.memory = collections.OrderedDict() 135b1742570SAlexander Graf self.data['memory'] = self.memory 136b1742570SAlexander Graf 137b1742570SAlexander Graf def __repr__(self): 138b1742570SAlexander Graf return self.data.__repr__() 139b1742570SAlexander Graf 140b1742570SAlexander Graf def __str__(self): 141b1742570SAlexander Graf return self.data.__str__() 142b1742570SAlexander Graf 143b1742570SAlexander Graf def getDict(self): 144b1742570SAlexander Graf return self.data 145b1742570SAlexander Graf 146b1742570SAlexander Graf def read(self): 147b1742570SAlexander Graf # Read all RAM sections 148b1742570SAlexander Graf while True: 149b1742570SAlexander Graf addr = self.file.read64() 150b1742570SAlexander Graf flags = addr & (self.TARGET_PAGE_SIZE - 1) 151b1742570SAlexander Graf addr &= ~(self.TARGET_PAGE_SIZE - 1) 152b1742570SAlexander Graf 153b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_MEM_SIZE: 154434b8adcSPeter Xu total_length = addr 155434b8adcSPeter Xu while total_length > 0: 156b1742570SAlexander Graf namelen = self.file.read8() 157b1742570SAlexander Graf self.name = self.file.readstr(len = namelen) 158b1742570SAlexander Graf len = self.file.read64() 159434b8adcSPeter Xu total_length -= len 160b1742570SAlexander Graf self.sizeinfo[self.name] = '0x%016x' % len 161b1742570SAlexander Graf if self.write_memory: 162f03868bdSEduardo Habkost print(self.name) 163b1742570SAlexander Graf mkdir_p('./' + os.path.dirname(self.name)) 164b1742570SAlexander Graf f = open('./' + self.name, "wb") 165b1742570SAlexander Graf f.truncate(0) 166b1742570SAlexander Graf f.truncate(len) 167b1742570SAlexander Graf self.files[self.name] = f 168ff40c7f0SFabiano Rosas if self.ignore_shared: 169ff40c7f0SFabiano Rosas mr_addr = self.file.read64() 170b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE 171b1742570SAlexander Graf 172b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_COMPRESS: 173b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_CONTINUE: 174b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_CONTINUE 175b1742570SAlexander Graf else: 176b1742570SAlexander Graf self.name = self.file.readstr() 177b1742570SAlexander Graf fill_char = self.file.read8() 178b1742570SAlexander Graf # The page in question is filled with fill_char now 179b1742570SAlexander Graf if self.write_memory and fill_char != 0: 180b1742570SAlexander Graf self.files[self.name].seek(addr, os.SEEK_SET) 181b1742570SAlexander Graf self.files[self.name].write(chr(fill_char) * self.TARGET_PAGE_SIZE) 182b1742570SAlexander Graf if self.dump_memory: 183b1742570SAlexander Graf self.memory['%s (0x%016x)' % (self.name, addr)] = 'Filled with 0x%02x' % fill_char 184b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_COMPRESS 185b1742570SAlexander Graf elif flags & self.RAM_SAVE_FLAG_PAGE: 186b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_CONTINUE: 187b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_CONTINUE 188b1742570SAlexander Graf else: 189b1742570SAlexander Graf self.name = self.file.readstr() 190b1742570SAlexander Graf 191b1742570SAlexander Graf if self.write_memory or self.dump_memory: 192b1742570SAlexander Graf data = self.file.readvar(size = self.TARGET_PAGE_SIZE) 193b1742570SAlexander Graf else: # Just skip RAM data 194b1742570SAlexander Graf self.file.file.seek(self.TARGET_PAGE_SIZE, 1) 195b1742570SAlexander Graf 196b1742570SAlexander Graf if self.write_memory: 197b1742570SAlexander Graf self.files[self.name].seek(addr, os.SEEK_SET) 198b1742570SAlexander Graf self.files[self.name].write(data) 199b1742570SAlexander Graf if self.dump_memory: 200b1742570SAlexander Graf hexdata = " ".join("{0:02x}".format(ord(c)) for c in data) 201b1742570SAlexander Graf self.memory['%s (0x%016x)' % (self.name, addr)] = hexdata 202b1742570SAlexander Graf 203b1742570SAlexander Graf flags &= ~self.RAM_SAVE_FLAG_PAGE 204b1742570SAlexander Graf elif flags & self.RAM_SAVE_FLAG_XBZRLE: 205b1742570SAlexander Graf raise Exception("XBZRLE RAM compression is not supported yet") 206b1742570SAlexander Graf elif flags & self.RAM_SAVE_FLAG_HOOK: 207b1742570SAlexander Graf raise Exception("RAM hooks don't make sense with files") 208f1de3097SMarc-André Lureau if flags & self.RAM_SAVE_FLAG_MULTIFD_FLUSH: 209f1de3097SMarc-André Lureau continue 210b1742570SAlexander Graf 211b1742570SAlexander Graf # End of RAM section 212b1742570SAlexander Graf if flags & self.RAM_SAVE_FLAG_EOS: 213b1742570SAlexander Graf break 214b1742570SAlexander Graf 215b1742570SAlexander Graf if flags != 0: 216b1742570SAlexander Graf raise Exception("Unknown RAM flags: %x" % flags) 217b1742570SAlexander Graf 218b1742570SAlexander Graf def __del__(self): 219b1742570SAlexander Graf if self.write_memory: 220b1742570SAlexander Graf for key in self.files: 221b1742570SAlexander Graf self.files[key].close() 222b1742570SAlexander Graf 223b1742570SAlexander Graf 224b1742570SAlexander Grafclass HTABSection(object): 225b1742570SAlexander Graf HASH_PTE_SIZE_64 = 16 226b1742570SAlexander Graf 227b1742570SAlexander Graf def __init__(self, file, version_id, device, section_key): 228b1742570SAlexander Graf if version_id != 1: 229b1742570SAlexander Graf raise Exception("Unknown HTAB version %d" % version_id) 230b1742570SAlexander Graf 231b1742570SAlexander Graf self.file = file 232b1742570SAlexander Graf self.section_key = section_key 233b1742570SAlexander Graf 234b1742570SAlexander Graf def read(self): 235b1742570SAlexander Graf 236b1742570SAlexander Graf header = self.file.read32() 237b1742570SAlexander Graf 238029ff892SLaurent Vivier if (header == -1): 239029ff892SLaurent Vivier # "no HPT" encoding 240029ff892SLaurent Vivier return 241029ff892SLaurent Vivier 242b1742570SAlexander Graf if (header > 0): 243b1742570SAlexander Graf # First section, just the hash shift 244b1742570SAlexander Graf return 245b1742570SAlexander Graf 246b1742570SAlexander Graf # Read until end marker 247b1742570SAlexander Graf while True: 248b1742570SAlexander Graf index = self.file.read32() 249b1742570SAlexander Graf n_valid = self.file.read16() 250b1742570SAlexander Graf n_invalid = self.file.read16() 251b1742570SAlexander Graf 252b1742570SAlexander Graf if index == 0 and n_valid == 0 and n_invalid == 0: 253b1742570SAlexander Graf break 254b1742570SAlexander Graf 255be7433efSGreg Kurz self.file.readvar(n_valid * self.HASH_PTE_SIZE_64) 256b1742570SAlexander Graf 257b1742570SAlexander Graf def getDict(self): 258b1742570SAlexander Graf return "" 259b1742570SAlexander Graf 26096e5c9bcSMark Cave-Ayland 26181c2c9ddSThomas Huthclass S390StorageAttributes(object): 26281c2c9ddSThomas Huth STATTR_FLAG_EOS = 0x01 26381c2c9ddSThomas Huth STATTR_FLAG_MORE = 0x02 26481c2c9ddSThomas Huth STATTR_FLAG_ERROR = 0x04 26581c2c9ddSThomas Huth STATTR_FLAG_DONE = 0x08 26681c2c9ddSThomas Huth 26781c2c9ddSThomas Huth def __init__(self, file, version_id, device, section_key): 26881c2c9ddSThomas Huth if version_id != 0: 26981c2c9ddSThomas Huth raise Exception("Unknown storage_attributes version %d" % version_id) 27081c2c9ddSThomas Huth 27181c2c9ddSThomas Huth self.file = file 27281c2c9ddSThomas Huth self.section_key = section_key 27381c2c9ddSThomas Huth 27481c2c9ddSThomas Huth def read(self): 27581c2c9ddSThomas Huth while True: 27681c2c9ddSThomas Huth addr_flags = self.file.read64() 27781c2c9ddSThomas Huth flags = addr_flags & 0xfff 27881c2c9ddSThomas Huth if (flags & (self.STATTR_FLAG_DONE | self.STATTR_FLAG_EOS)): 27981c2c9ddSThomas Huth return 28081c2c9ddSThomas Huth if (flags & self.STATTR_FLAG_ERROR): 28181c2c9ddSThomas Huth raise Exception("Error in migration stream") 28281c2c9ddSThomas Huth count = self.file.read64() 28381c2c9ddSThomas Huth self.file.readvar(count) 28481c2c9ddSThomas Huth 28581c2c9ddSThomas Huth def getDict(self): 28681c2c9ddSThomas Huth return "" 28781c2c9ddSThomas Huth 28881c2c9ddSThomas Huth 28996e5c9bcSMark Cave-Aylandclass ConfigurationSection(object): 290c36c31c8SFabiano Rosas def __init__(self, file, desc): 29196e5c9bcSMark Cave-Ayland self.file = file 292c36c31c8SFabiano Rosas self.desc = desc 29331499a9dSFabiano Rosas self.caps = [] 29431499a9dSFabiano Rosas 29531499a9dSFabiano Rosas def parse_capabilities(self, vmsd_caps): 29631499a9dSFabiano Rosas if not vmsd_caps: 29731499a9dSFabiano Rosas return 29831499a9dSFabiano Rosas 29931499a9dSFabiano Rosas ncaps = vmsd_caps.data['caps_count'].data 30031499a9dSFabiano Rosas self.caps = vmsd_caps.data['capabilities'] 30131499a9dSFabiano Rosas 30231499a9dSFabiano Rosas if type(self.caps) != list: 30331499a9dSFabiano Rosas self.caps = [self.caps] 30431499a9dSFabiano Rosas 30531499a9dSFabiano Rosas if len(self.caps) != ncaps: 30631499a9dSFabiano Rosas raise Exception("Number of capabilities doesn't match " 30731499a9dSFabiano Rosas "caps_count field") 30831499a9dSFabiano Rosas 30931499a9dSFabiano Rosas def has_capability(self, cap): 31031499a9dSFabiano Rosas return any([str(c) == cap for c in self.caps]) 31196e5c9bcSMark Cave-Ayland 31296e5c9bcSMark Cave-Ayland def read(self): 313c36c31c8SFabiano Rosas if self.desc: 314c36c31c8SFabiano Rosas version_id = self.desc['version'] 315c36c31c8SFabiano Rosas section = VMSDSection(self.file, version_id, self.desc, 316c36c31c8SFabiano Rosas 'configuration') 317c36c31c8SFabiano Rosas section.read() 31831499a9dSFabiano Rosas self.parse_capabilities( 31931499a9dSFabiano Rosas section.data.get("configuration/capabilities")) 320c36c31c8SFabiano Rosas else: 321c36c31c8SFabiano Rosas # backward compatibility for older streams that don't have 322c36c31c8SFabiano Rosas # the configuration section in the json 32396e5c9bcSMark Cave-Ayland name_len = self.file.read32() 32496e5c9bcSMark Cave-Ayland name = self.file.readstr(len = name_len) 32596e5c9bcSMark Cave-Ayland 326b1742570SAlexander Grafclass VMSDFieldGeneric(object): 327b1742570SAlexander Graf def __init__(self, desc, file): 328b1742570SAlexander Graf self.file = file 329b1742570SAlexander Graf self.desc = desc 330b1742570SAlexander Graf self.data = "" 331b1742570SAlexander Graf 332b1742570SAlexander Graf def __repr__(self): 333b1742570SAlexander Graf return str(self.__str__()) 334b1742570SAlexander Graf 335b1742570SAlexander Graf def __str__(self): 33683de0ea4SMarc-André Lureau return " ".join("{0:02x}".format(c) for c in self.data) 337b1742570SAlexander Graf 338b1742570SAlexander Graf def getDict(self): 339b1742570SAlexander Graf return self.__str__() 340b1742570SAlexander Graf 341b1742570SAlexander Graf def read(self): 342b1742570SAlexander Graf size = int(self.desc['size']) 343b1742570SAlexander Graf self.data = self.file.readvar(size) 344b1742570SAlexander Graf return self.data 345b1742570SAlexander Graf 34631499a9dSFabiano Rosasclass VMSDFieldCap(object): 34731499a9dSFabiano Rosas def __init__(self, desc, file): 34831499a9dSFabiano Rosas self.file = file 34931499a9dSFabiano Rosas self.desc = desc 35031499a9dSFabiano Rosas self.data = "" 35131499a9dSFabiano Rosas 35231499a9dSFabiano Rosas def __repr__(self): 35331499a9dSFabiano Rosas return self.data 35431499a9dSFabiano Rosas 35531499a9dSFabiano Rosas def __str__(self): 35631499a9dSFabiano Rosas return self.data 35731499a9dSFabiano Rosas 35831499a9dSFabiano Rosas def read(self): 35931499a9dSFabiano Rosas len = self.file.read8() 36031499a9dSFabiano Rosas self.data = self.file.readstr(len) 36131499a9dSFabiano Rosas 36231499a9dSFabiano Rosas 363b1742570SAlexander Grafclass VMSDFieldInt(VMSDFieldGeneric): 364b1742570SAlexander Graf def __init__(self, desc, file): 365b1742570SAlexander Graf super(VMSDFieldInt, self).__init__(desc, file) 366b1742570SAlexander Graf self.size = int(desc['size']) 367b1742570SAlexander Graf self.format = '0x%%0%dx' % (self.size * 2) 368b1742570SAlexander Graf self.sdtype = '>i%d' % self.size 369b1742570SAlexander Graf self.udtype = '>u%d' % self.size 370b1742570SAlexander Graf 371b1742570SAlexander Graf def __repr__(self): 372b1742570SAlexander Graf if self.data < 0: 373b1742570SAlexander Graf return ('%s (%d)' % ((self.format % self.udata), self.data)) 374b1742570SAlexander Graf else: 375b1742570SAlexander Graf return self.format % self.data 376b1742570SAlexander Graf 377b1742570SAlexander Graf def __str__(self): 378b1742570SAlexander Graf return self.__repr__() 379b1742570SAlexander Graf 380b1742570SAlexander Graf def getDict(self): 381b1742570SAlexander Graf return self.__str__() 382b1742570SAlexander Graf 383b1742570SAlexander Graf def read(self): 384b1742570SAlexander Graf super(VMSDFieldInt, self).read() 38583de0ea4SMarc-André Lureau self.sdata = int.from_bytes(self.data, byteorder='big', signed=True) 38683de0ea4SMarc-André Lureau self.udata = int.from_bytes(self.data, byteorder='big', signed=False) 387b1742570SAlexander Graf self.data = self.sdata 388b1742570SAlexander Graf return self.data 389b1742570SAlexander Graf 390b1742570SAlexander Grafclass VMSDFieldUInt(VMSDFieldInt): 391b1742570SAlexander Graf def __init__(self, desc, file): 392b1742570SAlexander Graf super(VMSDFieldUInt, self).__init__(desc, file) 393b1742570SAlexander Graf 394b1742570SAlexander Graf def read(self): 395b1742570SAlexander Graf super(VMSDFieldUInt, self).read() 396b1742570SAlexander Graf self.data = self.udata 397b1742570SAlexander Graf return self.data 398b1742570SAlexander Graf 399b1742570SAlexander Grafclass VMSDFieldIntLE(VMSDFieldInt): 400b1742570SAlexander Graf def __init__(self, desc, file): 401b1742570SAlexander Graf super(VMSDFieldIntLE, self).__init__(desc, file) 402b1742570SAlexander Graf self.dtype = '<i%d' % self.size 403b1742570SAlexander Graf 404b1742570SAlexander Grafclass VMSDFieldBool(VMSDFieldGeneric): 405b1742570SAlexander Graf def __init__(self, desc, file): 406b1742570SAlexander Graf super(VMSDFieldBool, self).__init__(desc, file) 407b1742570SAlexander Graf 408b1742570SAlexander Graf def __repr__(self): 409b1742570SAlexander Graf return self.data.__repr__() 410b1742570SAlexander Graf 411b1742570SAlexander Graf def __str__(self): 412b1742570SAlexander Graf return self.data.__str__() 413b1742570SAlexander Graf 414b1742570SAlexander Graf def getDict(self): 415b1742570SAlexander Graf return self.data 416b1742570SAlexander Graf 417b1742570SAlexander Graf def read(self): 418b1742570SAlexander Graf super(VMSDFieldBool, self).read() 419b1742570SAlexander Graf if self.data[0] == 0: 420b1742570SAlexander Graf self.data = False 421b1742570SAlexander Graf else: 422b1742570SAlexander Graf self.data = True 423b1742570SAlexander Graf return self.data 424b1742570SAlexander Graf 425b1742570SAlexander Grafclass VMSDFieldStruct(VMSDFieldGeneric): 426b1742570SAlexander Graf QEMU_VM_SUBSECTION = 0x05 427b1742570SAlexander Graf 428b1742570SAlexander Graf def __init__(self, desc, file): 429b1742570SAlexander Graf super(VMSDFieldStruct, self).__init__(desc, file) 430b1742570SAlexander Graf self.data = collections.OrderedDict() 431b1742570SAlexander Graf 432b1742570SAlexander Graf # When we see compressed array elements, unfold them here 433b1742570SAlexander Graf new_fields = [] 434b1742570SAlexander Graf for field in self.desc['struct']['fields']: 435b1742570SAlexander Graf if not 'array_len' in field: 436b1742570SAlexander Graf new_fields.append(field) 437b1742570SAlexander Graf continue 438b1742570SAlexander Graf array_len = field.pop('array_len') 439b1742570SAlexander Graf field['index'] = 0 440b1742570SAlexander Graf new_fields.append(field) 44183de0ea4SMarc-André Lureau for i in range(1, array_len): 442b1742570SAlexander Graf c = field.copy() 443b1742570SAlexander Graf c['index'] = i 444b1742570SAlexander Graf new_fields.append(c) 445b1742570SAlexander Graf 446b1742570SAlexander Graf self.desc['struct']['fields'] = new_fields 447b1742570SAlexander Graf 448b1742570SAlexander Graf def __repr__(self): 449b1742570SAlexander Graf return self.data.__repr__() 450b1742570SAlexander Graf 451b1742570SAlexander Graf def __str__(self): 452b1742570SAlexander Graf return self.data.__str__() 453b1742570SAlexander Graf 454b1742570SAlexander Graf def read(self): 455b1742570SAlexander Graf for field in self.desc['struct']['fields']: 456b1742570SAlexander Graf try: 457b1742570SAlexander Graf reader = vmsd_field_readers[field['type']] 458b1742570SAlexander Graf except: 459b1742570SAlexander Graf reader = VMSDFieldGeneric 460b1742570SAlexander Graf 461b1742570SAlexander Graf field['data'] = reader(field, self.file) 462b1742570SAlexander Graf field['data'].read() 463b1742570SAlexander Graf 464b1742570SAlexander Graf if 'index' in field: 465b1742570SAlexander Graf if field['name'] not in self.data: 466b1742570SAlexander Graf self.data[field['name']] = [] 467b1742570SAlexander Graf a = self.data[field['name']] 468b1742570SAlexander Graf if len(a) != int(field['index']): 469b1742570SAlexander Graf raise Exception("internal index of data field unmatched (%d/%d)" % (len(a), int(field['index']))) 470b1742570SAlexander Graf a.append(field['data']) 471b1742570SAlexander Graf else: 472b1742570SAlexander Graf self.data[field['name']] = field['data'] 473b1742570SAlexander Graf 474b1742570SAlexander Graf if 'subsections' in self.desc['struct']: 475b1742570SAlexander Graf for subsection in self.desc['struct']['subsections']: 476b1742570SAlexander Graf if self.file.read8() != self.QEMU_VM_SUBSECTION: 477b1742570SAlexander Graf raise Exception("Subsection %s not found at offset %x" % ( subsection['vmsd_name'], self.file.tell())) 478b1742570SAlexander Graf name = self.file.readstr() 479b1742570SAlexander Graf version_id = self.file.read32() 480b1742570SAlexander Graf self.data[name] = VMSDSection(self.file, version_id, subsection, (name, 0)) 481b1742570SAlexander Graf self.data[name].read() 482b1742570SAlexander Graf 483b1742570SAlexander Graf def getDictItem(self, value): 484b1742570SAlexander Graf # Strings would fall into the array category, treat 485b1742570SAlexander Graf # them specially 486b1742570SAlexander Graf if value.__class__ is ''.__class__: 487b1742570SAlexander Graf return value 488b1742570SAlexander Graf 489b1742570SAlexander Graf try: 490b1742570SAlexander Graf return self.getDictOrderedDict(value) 491b1742570SAlexander Graf except: 492b1742570SAlexander Graf try: 493b1742570SAlexander Graf return self.getDictArray(value) 494b1742570SAlexander Graf except: 495b1742570SAlexander Graf try: 496b1742570SAlexander Graf return value.getDict() 497b1742570SAlexander Graf except: 498b1742570SAlexander Graf return value 499b1742570SAlexander Graf 500b1742570SAlexander Graf def getDictArray(self, array): 501b1742570SAlexander Graf r = [] 502b1742570SAlexander Graf for value in array: 503b1742570SAlexander Graf r.append(self.getDictItem(value)) 504b1742570SAlexander Graf return r 505b1742570SAlexander Graf 506b1742570SAlexander Graf def getDictOrderedDict(self, dict): 507b1742570SAlexander Graf r = collections.OrderedDict() 508b1742570SAlexander Graf for (key, value) in dict.items(): 509b1742570SAlexander Graf r[key] = self.getDictItem(value) 510b1742570SAlexander Graf return r 511b1742570SAlexander Graf 512b1742570SAlexander Graf def getDict(self): 513b1742570SAlexander Graf return self.getDictOrderedDict(self.data) 514b1742570SAlexander Graf 515b1742570SAlexander Grafvmsd_field_readers = { 516b1742570SAlexander Graf "bool" : VMSDFieldBool, 517b1742570SAlexander Graf "int8" : VMSDFieldInt, 518b1742570SAlexander Graf "int16" : VMSDFieldInt, 519b1742570SAlexander Graf "int32" : VMSDFieldInt, 520b1742570SAlexander Graf "int32 equal" : VMSDFieldInt, 521b1742570SAlexander Graf "int32 le" : VMSDFieldIntLE, 522b1742570SAlexander Graf "int64" : VMSDFieldInt, 523b1742570SAlexander Graf "uint8" : VMSDFieldUInt, 524b1742570SAlexander Graf "uint16" : VMSDFieldUInt, 525b1742570SAlexander Graf "uint32" : VMSDFieldUInt, 526b1742570SAlexander Graf "uint32 equal" : VMSDFieldUInt, 527b1742570SAlexander Graf "uint64" : VMSDFieldUInt, 528b1742570SAlexander Graf "int64 equal" : VMSDFieldInt, 529b1742570SAlexander Graf "uint8 equal" : VMSDFieldInt, 530b1742570SAlexander Graf "uint16 equal" : VMSDFieldInt, 531b1742570SAlexander Graf "float64" : VMSDFieldGeneric, 532b1742570SAlexander Graf "timer" : VMSDFieldGeneric, 533b1742570SAlexander Graf "buffer" : VMSDFieldGeneric, 534b1742570SAlexander Graf "unused_buffer" : VMSDFieldGeneric, 535b1742570SAlexander Graf "bitmap" : VMSDFieldGeneric, 536b1742570SAlexander Graf "struct" : VMSDFieldStruct, 53731499a9dSFabiano Rosas "capability": VMSDFieldCap, 538b1742570SAlexander Graf "unknown" : VMSDFieldGeneric, 539b1742570SAlexander Graf} 540b1742570SAlexander Graf 541b1742570SAlexander Grafclass VMSDSection(VMSDFieldStruct): 542b1742570SAlexander Graf def __init__(self, file, version_id, device, section_key): 543b1742570SAlexander Graf self.file = file 544b1742570SAlexander Graf self.data = "" 545b1742570SAlexander Graf self.vmsd_name = "" 546b1742570SAlexander Graf self.section_key = section_key 547b1742570SAlexander Graf desc = device 548b1742570SAlexander Graf if 'vmsd_name' in device: 549b1742570SAlexander Graf self.vmsd_name = device['vmsd_name'] 550b1742570SAlexander Graf 551b1742570SAlexander Graf # A section really is nothing but a FieldStruct :) 552b1742570SAlexander Graf super(VMSDSection, self).__init__({ 'struct' : desc }, file) 553b1742570SAlexander Graf 554b1742570SAlexander Graf############################################################################### 555b1742570SAlexander Graf 556b1742570SAlexander Grafclass MigrationDump(object): 557b1742570SAlexander Graf QEMU_VM_FILE_MAGIC = 0x5145564d 558b1742570SAlexander Graf QEMU_VM_FILE_VERSION = 0x00000003 559b1742570SAlexander Graf QEMU_VM_EOF = 0x00 560b1742570SAlexander Graf QEMU_VM_SECTION_START = 0x01 561b1742570SAlexander Graf QEMU_VM_SECTION_PART = 0x02 562b1742570SAlexander Graf QEMU_VM_SECTION_END = 0x03 563b1742570SAlexander Graf QEMU_VM_SECTION_FULL = 0x04 564b1742570SAlexander Graf QEMU_VM_SUBSECTION = 0x05 565b1742570SAlexander Graf QEMU_VM_VMDESCRIPTION = 0x06 56696e5c9bcSMark Cave-Ayland QEMU_VM_CONFIGURATION = 0x07 56773d9a796SDr. David Alan Gilbert QEMU_VM_SECTION_FOOTER= 0x7e 568b1742570SAlexander Graf 569b1742570SAlexander Graf def __init__(self, filename): 57081c2c9ddSThomas Huth self.section_classes = { 57181c2c9ddSThomas Huth ( 'ram', 0 ) : [ RamSection, None ], 57281c2c9ddSThomas Huth ( 's390-storage_attributes', 0 ) : [ S390StorageAttributes, None], 57381c2c9ddSThomas Huth ( 'spapr/htab', 0) : ( HTABSection, None ) 57481c2c9ddSThomas Huth } 575b1742570SAlexander Graf self.filename = filename 576b1742570SAlexander Graf self.vmsd_desc = None 577b1742570SAlexander Graf 578b1742570SAlexander Graf def read(self, desc_only = False, dump_memory = False, write_memory = False): 579b1742570SAlexander Graf # Read in the whole file 580b1742570SAlexander Graf file = MigrationFile(self.filename) 581b1742570SAlexander Graf 582b1742570SAlexander Graf # File magic 583b1742570SAlexander Graf data = file.read32() 584b1742570SAlexander Graf if data != self.QEMU_VM_FILE_MAGIC: 585b1742570SAlexander Graf raise Exception("Invalid file magic %x" % data) 586b1742570SAlexander Graf 587b1742570SAlexander Graf # Version (has to be v3) 588b1742570SAlexander Graf data = file.read32() 589b1742570SAlexander Graf if data != self.QEMU_VM_FILE_VERSION: 590b1742570SAlexander Graf raise Exception("Invalid version number %d" % data) 591b1742570SAlexander Graf 592b1742570SAlexander Graf self.load_vmsd_json(file) 593b1742570SAlexander Graf 594b1742570SAlexander Graf # Read sections 595b1742570SAlexander Graf self.sections = collections.OrderedDict() 596b1742570SAlexander Graf 597b1742570SAlexander Graf if desc_only: 598b1742570SAlexander Graf return 599b1742570SAlexander Graf 600b1742570SAlexander Graf ramargs = {} 601b1742570SAlexander Graf ramargs['page_size'] = self.vmsd_desc['page_size'] 602b1742570SAlexander Graf ramargs['dump_memory'] = dump_memory 603b1742570SAlexander Graf ramargs['write_memory'] = write_memory 604ff40c7f0SFabiano Rosas ramargs['ignore_shared'] = False 605b1742570SAlexander Graf self.section_classes[('ram',0)][1] = ramargs 606b1742570SAlexander Graf 607b1742570SAlexander Graf while True: 608b1742570SAlexander Graf section_type = file.read8() 609b1742570SAlexander Graf if section_type == self.QEMU_VM_EOF: 610b1742570SAlexander Graf break 61196e5c9bcSMark Cave-Ayland elif section_type == self.QEMU_VM_CONFIGURATION: 612c36c31c8SFabiano Rosas config_desc = self.vmsd_desc.get('configuration') 613c36c31c8SFabiano Rosas section = ConfigurationSection(file, config_desc) 61496e5c9bcSMark Cave-Ayland section.read() 615ff40c7f0SFabiano Rosas ramargs['ignore_shared'] = section.has_capability('x-ignore-shared') 616b1742570SAlexander Graf elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL: 617b1742570SAlexander Graf section_id = file.read32() 618b1742570SAlexander Graf name = file.readstr() 619b1742570SAlexander Graf instance_id = file.read32() 620b1742570SAlexander Graf version_id = file.read32() 621b1742570SAlexander Graf section_key = (name, instance_id) 622b1742570SAlexander Graf classdesc = self.section_classes[section_key] 623b1742570SAlexander Graf section = classdesc[0](file, version_id, classdesc[1], section_key) 624b1742570SAlexander Graf self.sections[section_id] = section 625b1742570SAlexander Graf section.read() 626b1742570SAlexander Graf elif section_type == self.QEMU_VM_SECTION_PART or section_type == self.QEMU_VM_SECTION_END: 627b1742570SAlexander Graf section_id = file.read32() 628b1742570SAlexander Graf self.sections[section_id].read() 62973d9a796SDr. David Alan Gilbert elif section_type == self.QEMU_VM_SECTION_FOOTER: 63073d9a796SDr. David Alan Gilbert read_section_id = file.read32() 63173d9a796SDr. David Alan Gilbert if read_section_id != section_id: 63273d9a796SDr. David Alan Gilbert raise Exception("Mismatched section footer: %x vs %x" % (read_section_id, section_id)) 633b1742570SAlexander Graf else: 634b1742570SAlexander Graf raise Exception("Unknown section type: %d" % section_type) 635b1742570SAlexander Graf file.close() 636b1742570SAlexander Graf 637b1742570SAlexander Graf def load_vmsd_json(self, file): 638b1742570SAlexander Graf vmsd_json = file.read_migration_debug_json() 639b1742570SAlexander Graf self.vmsd_desc = json.loads(vmsd_json, object_pairs_hook=collections.OrderedDict) 640b1742570SAlexander Graf for device in self.vmsd_desc['devices']: 641b1742570SAlexander Graf key = (device['name'], device['instance_id']) 642b1742570SAlexander Graf value = ( VMSDSection, device ) 643b1742570SAlexander Graf self.section_classes[key] = value 644b1742570SAlexander Graf 645b1742570SAlexander Graf def getDict(self): 646b1742570SAlexander Graf r = collections.OrderedDict() 647b1742570SAlexander Graf for (key, value) in self.sections.items(): 648b1742570SAlexander Graf key = "%s (%d)" % ( value.section_key[0], key ) 649b1742570SAlexander Graf r[key] = value.getDict() 650b1742570SAlexander Graf return r 651b1742570SAlexander Graf 652b1742570SAlexander Graf############################################################################### 653b1742570SAlexander Graf 654b1742570SAlexander Grafclass JSONEncoder(json.JSONEncoder): 655b1742570SAlexander Graf def default(self, o): 656b1742570SAlexander Graf if isinstance(o, VMSDFieldGeneric): 657b1742570SAlexander Graf return str(o) 658b1742570SAlexander Graf return json.JSONEncoder.default(self, o) 659b1742570SAlexander Graf 660b1742570SAlexander Grafparser = argparse.ArgumentParser() 661b1742570SAlexander Grafparser.add_argument("-f", "--file", help='migration dump to read from', required=True) 662b1742570SAlexander Grafparser.add_argument("-m", "--memory", help='dump RAM contents as well', action='store_true') 663b1742570SAlexander Grafparser.add_argument("-d", "--dump", help='what to dump ("state" or "desc")', default='state') 664b1742570SAlexander Grafparser.add_argument("-x", "--extract", help='extract contents into individual files', action='store_true') 665b1742570SAlexander Grafargs = parser.parse_args() 666b1742570SAlexander Graf 667b1742570SAlexander Grafjsonenc = JSONEncoder(indent=4, separators=(',', ': ')) 668b1742570SAlexander Graf 669b1742570SAlexander Grafif args.extract: 670b1742570SAlexander Graf dump = MigrationDump(args.file) 671b1742570SAlexander Graf 672b1742570SAlexander Graf dump.read(desc_only = True) 673f03868bdSEduardo Habkost print("desc.json") 6742c92be50SLaurent Vivier f = open("desc.json", "w") 675b1742570SAlexander Graf f.truncate() 676b1742570SAlexander Graf f.write(jsonenc.encode(dump.vmsd_desc)) 677b1742570SAlexander Graf f.close() 678b1742570SAlexander Graf 679b1742570SAlexander Graf dump.read(write_memory = True) 680b1742570SAlexander Graf dict = dump.getDict() 681f03868bdSEduardo Habkost print("state.json") 6822c92be50SLaurent Vivier f = open("state.json", "w") 683b1742570SAlexander Graf f.truncate() 684b1742570SAlexander Graf f.write(jsonenc.encode(dict)) 685b1742570SAlexander Graf f.close() 686b1742570SAlexander Grafelif args.dump == "state": 687b1742570SAlexander Graf dump = MigrationDump(args.file) 688b1742570SAlexander Graf dump.read(dump_memory = args.memory) 689b1742570SAlexander Graf dict = dump.getDict() 690f03868bdSEduardo Habkost print(jsonenc.encode(dict)) 691b1742570SAlexander Grafelif args.dump == "desc": 692b1742570SAlexander Graf dump = MigrationDump(args.file) 693b1742570SAlexander Graf dump.read(desc_only = True) 694f03868bdSEduardo Habkost print(jsonenc.encode(dump.vmsd_desc)) 695b1742570SAlexander Grafelse: 696f98d372aSLaurent Vivier raise Exception("Please specify either -x, -d state or -d desc") 697