From: Pawel Zmarzly Date: Thu, 13 Nov 2025 18:17:08 +0000 (-0500) Subject: scripts/analyze-migration: Support mapped-ram snapshot format X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0002b0db1377808e3c2abcb4f02f3d3aa8ce304a;p=thirdparty%2Fqemu.git scripts/analyze-migration: Support mapped-ram snapshot format The script has not been updated to read mapped-ram snapshots and is currently crashing when trying to read such a file. With this commit, it can now read a snapshot created with: (qemu) migrate_set_capability x-ignore-shared on (qemu) migrate_set_capability mapped-ram on (qemu) migrate -d file:vm.state Signed-off-by: Pawel Zmarzly Link: https://lore.kernel.org/r/20251126155015.941129-1-pzmarzly0@gmail.com [peterx: space fixes, introduce parseMappedRamBlob(), add comments, etc.] Signed-off-by: Peter Xu --- diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index a12ea9fc8f..e81deab8f9 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -19,6 +19,7 @@ import json import os +import math import argparse import collections import struct @@ -127,6 +128,7 @@ class RamSection(object): self.dump_memory = ramargs['dump_memory'] self.write_memory = ramargs['write_memory'] self.ignore_shared = ramargs['ignore_shared'] + self.mapped_ram = ramargs['mapped_ram'] self.sizeinfo = collections.OrderedDict() self.data = collections.OrderedDict() self.data['section sizes'] = self.sizeinfo @@ -146,6 +148,57 @@ class RamSection(object): def getDict(self): return self.data + def parseMappedRamBlob(self, len): + version = self.file.read32() + if version != 1: + raise Exception("Unsupported MappedRamHeader version %s" % version) + + page_size = self.file.read64() + if page_size != self.TARGET_PAGE_SIZE: + raise Exception("Page size mismatch in MappedRamHeader") + + bitmap_offset = self.file.read64() + pages_offset = self.file.read64() + + if self.ignore_shared and bitmap_offset == 0 and pages_offset == 0: + # This is a shared ramblock, x-ignore-share must have been + # enabled, and mapped-ram didn't allocate bitmap or page blob + # for it. + return + + if self.dump_memory or self.write_memory: + num_pages = len // page_size + + self.file.seek(bitmap_offset, os.SEEK_SET) + bitmap_len = int(math.ceil(num_pages / 8)) + bitmap = self.file.readvar(size=bitmap_len) + + self.file.seek(pages_offset, os.SEEK_SET) + for page_num in range(num_pages): + page_addr = page_num * page_size + + is_filled = (bitmap[page_num // 8] >> page_num % 8) & 1 + if is_filled: + data = self.file.readvar(size=self.TARGET_PAGE_SIZE) + if self.write_memory: + self.files[self.name].seek(page_addr, os.SEEK_SET) + self.files[self.name].write(data) + if self.dump_memory: + hexdata = " ".join("{0:02x}".format(c) for c in data) + self.memory['%s (0x%016x)' % + (self.name, page_addr)] = hexdata + else: + self.file.seek(self.TARGET_PAGE_SIZE, os.SEEK_CUR) + if self.write_memory: + self.files[self.name].seek(page_addr, os.SEEK_SET) + self.files[self.name].write( + b'\x00' * self.TARGET_PAGE_SIZE) + if self.dump_memory: + self.memory['%s (0x%016x)' % + (self.name, page_addr)] = 'Filled with 0x00' + + self.file.seek(pages_offset + len, os.SEEK_SET) + def read(self): # Read all RAM sections while True: @@ -170,6 +223,8 @@ class RamSection(object): self.files[self.name] = f if self.ignore_shared: mr_addr = self.file.read64() + if self.mapped_ram: + self.parseMappedRamBlob(len) flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE if flags & self.RAM_SAVE_FLAG_ZERO: @@ -660,6 +715,7 @@ class MigrationDump(object): ramargs['dump_memory'] = dump_memory ramargs['write_memory'] = write_memory ramargs['ignore_shared'] = False + ramargs['mapped_ram'] = False self.section_classes[('ram',0)][1] = ramargs while True: @@ -671,6 +727,7 @@ class MigrationDump(object): section = ConfigurationSection(file, config_desc) section.read() ramargs['ignore_shared'] = section.has_capability('x-ignore-shared') + ramargs['mapped_ram'] = section.has_capability('mapped-ram') elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL: section_id = file.read32() name = file.readstr()