tests/qemu-iotests/qcow2.py | 149 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 8 deletions(-)
Add dirty bitmap information to QCOW2 metadata dump in qcow2.py script.
The sample output:
Header extension (Bitmaps):
magic 0x23852875
length 24
nb_bitmaps 2
reserved32 0
bitmap_directory_size 0x40
bitmap_directory_offset 0x100000
Bitmap name bitmap-1
flag "auto"
bitmap_table_offset 0x90000
bitmap_table_size 8
flags 2
type 1
granularity_bits 15
name_size 8
extra_data_size 0
Bitmap table
0 serialized, offset 0xa0000
1 all-zeroes, offset 0x0
2 all-zeroes, offset 0x0
3 all-zeroes, offset 0x0
4 all-zeroes, offset 0x0
5 all-zeroes, offset 0x0
6 all-zeroes, offset 0x0
7 all-zeroes, offset 0x0
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
tests/qemu-iotests/qcow2.py | 149 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 141 insertions(+), 8 deletions(-)
diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py
index 91e4420..41aa030 100755
--- a/tests/qemu-iotests/qcow2.py
+++ b/tests/qemu-iotests/qcow2.py
@@ -5,6 +5,122 @@ import sys
import struct
import string
+
+class Qcow2BitmapDirEntry:
+
+ name = ''
+ BME_FLAG_IN_USE = 1
+ BME_FLAG_AUTO = 1 << 1
+
+ uint8_t = 'B'
+ uint16_t = 'H'
+ uint32_t = 'I'
+ uint64_t = 'Q'
+
+ fields = [
+ [uint64_t, '%#x', 'bitmap_table_offset'],
+ [uint32_t, '%d', 'bitmap_table_size'],
+ [uint32_t, '%d', 'flags'],
+ [uint8_t, '%d', 'type'],
+ [uint8_t, '%d', 'granularity_bits'],
+ [uint16_t, '%d', 'name_size'],
+ [uint32_t, '%d', 'extra_data_size']
+ ]
+
+ fmt = '>' + ''.join(field[0] for field in fields)
+
+ def __init__(self, data):
+
+ entry = struct.unpack(Qcow2BitmapDirEntry.fmt, data)
+ self.__dict__ = dict((field[2], entry[i])
+ for i, field in enumerate(
+ Qcow2BitmapDirEntry.fields))
+
+ self.bitmap_table_size = self.bitmap_table_size \
+ * struct.calcsize(self.uint64_t)
+
+ def bitmap_dir_entry_size(self):
+ size = struct.calcsize(self.fmt) + self.name_size + \
+ self.extra_data_size
+ return (size + 7) & ~7
+
+ def dump_bitmap_dir_entry(self):
+ print("%-25s" % 'Bitmap name', self.name)
+ if (self.flags & self.BME_FLAG_IN_USE) != 0:
+ print("%-25s" % 'flag', '"in-use"')
+ if (self.flags & self.BME_FLAG_AUTO) != 0:
+ print("%-25s" % 'flag', '"auto"')
+ for f in Qcow2BitmapDirEntry.fields:
+ value = self.__dict__[f[2]]
+ value_str = f[1] % value
+
+ print("%-25s" % f[2], value_str)
+ print("")
+
+ def dump_bitmap_table(self, fd):
+ fd.seek(self.bitmap_table_offset)
+ table_size = self.bitmap_table_size * struct.calcsize(self.uint64_t)
+ bitmap_table = [e[0] for e in struct.iter_unpack('>Q',
+ fd.read(table_size))]
+ BME_TABLE_ENTRY_OFFSET_MASK = 0x00fffffffffffe00
+ BME_TABLE_ENTRY_FLAG_ALL_ONES = 1
+ bmt_type = ['all-zeroes', 'all-ones', 'serialized']
+ items = enumerate(bitmap_table)
+ print("Bitmap table")
+ for i, entry in items:
+ offset = entry & BME_TABLE_ENTRY_OFFSET_MASK
+ if offset != 0:
+ index = 2
+ else:
+ index = entry & BME_TABLE_ENTRY_FLAG_ALL_ONES
+ print(" %-4d %s, offset %#x" % (i, bmt_type[index], offset))
+ print("")
+
+
+class Qcow2BitmapExt:
+
+ uint32_t = 'I'
+ uint64_t = 'Q'
+
+ fields = [
+ [uint32_t, '%d', 'nb_bitmaps'],
+ [uint32_t, '%d', 'reserved32'],
+ [uint64_t, '%#x', 'bitmap_directory_size'],
+ [uint64_t, '%#x', 'bitmap_directory_offset']
+ ]
+
+ fmt = '>' + ''.join(field[0] for field in fields)
+
+ def __init__(self, data):
+
+ extension = struct.unpack(Qcow2BitmapExt.fmt, data)
+ self.__dict__ = dict((field[2], extension[i])
+ for i, field in enumerate(Qcow2BitmapExt.fields))
+
+ def dump_bitmap_ext(self):
+ for f in Qcow2BitmapExt.fields:
+ value = self.__dict__[f[2]]
+ value_str = f[1] % value
+
+ print("%-25s" % f[2], value_str)
+ print("")
+
+ def bitmap_directory(self, fd):
+ offset = self.bitmap_directory_offset
+ buf_size = struct.calcsize(Qcow2BitmapDirEntry.fmt)
+
+ for n in range(self.nb_bitmaps):
+ fd.seek(offset)
+ buf = fd.read(buf_size)
+ dir_entry = Qcow2BitmapDirEntry(buf)
+ fd.seek(dir_entry.extra_data_size, 1)
+ bitmap_name = fd.read(dir_entry.name_size)
+ dir_entry.name = bitmap_name.decode('ascii')
+ dir_entry.dump_bitmap_dir_entry()
+ dir_entry.dump_bitmap_table(fd)
+ offset += dir_entry.bitmap_dir_entry_size()
+
+
class QcowHeaderExtension:
def __init__(self, magic, length, data):
@@ -22,6 +138,8 @@ class QcowHeaderExtension:
class QcowHeader:
+ QCOW2_EXT_MAGIC_FEATURE_TABLE = 0x6803f857
+ QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
uint32_t = 'I'
uint64_t = 'Q'
@@ -128,6 +246,12 @@ class QcowHeader:
buf = buf[0:header_bytes-1]
fd.write(buf)
+ def extension_name(self, magic):
+ return {
+ self.QCOW2_EXT_MAGIC_FEATURE_TABLE: 'Feature table',
+ self.QCOW2_EXT_MAGIC_BITMAPS: 'Bitmaps',
+ }.get(magic, 'Unknown')
+
def dump(self):
for f in QcowHeader.fields:
value = self.__dict__[f[2]]
@@ -143,30 +267,39 @@ class QcowHeader:
print("%-25s" % f[2], value_str)
print("")
- def dump_extensions(self):
+ def dump_extensions(self, fd):
for ex in self.extensions:
+ print("Header extension (%s):" % self.extension_name(ex.magic))
+ print("%-25s %#x" % ("magic", ex.magic))
+ print("%-25s %d" % ("length", ex.length))
+
data = ex.data[:ex.length]
if all(c in string.printable.encode('ascii') for c in data):
data = "'%s'" % data.decode('ascii')
+ print("%-25s %s" % ("data", data))
else:
- data = "<binary>"
+ self.dump_extension_data(fd, ex)
- print("Header extension:")
- print("%-25s %#x" % ("magic", ex.magic))
- print("%-25s %d" % ("length", ex.length))
- print("%-25s %s" % ("data", data))
print("")
+ def dump_extension_data(self, fd, ext):
+ if ext.magic == self.QCOW2_EXT_MAGIC_BITMAPS:
+ b_ext = Qcow2BitmapExt(ext.data)
+ b_ext.dump_bitmap_ext()
+ b_ext.bitmap_directory(fd)
+ else:
+ print("%-25s %s" % ("data", "<binary>"))
+
def cmd_dump_header(fd):
h = QcowHeader(fd)
h.dump()
- h.dump_extensions()
+ h.dump_extensions(fd)
def cmd_dump_header_exts(fd):
h = QcowHeader(fd)
- h.dump_extensions()
+ h.dump_extensions(fd)
def cmd_set_header(fd, name, value):
try:
--
1.8.3.1
Patchew URL: https://patchew.org/QEMU/1590504866-679474-1-git-send-email-andrey.shinkevich@virtuozzo.com/ Hi, This series failed the docker-quick@centos7 build test. Please find the testing commands and their output below. If you have Docker installed, you can probably reproduce it locally. === TEST SCRIPT BEGIN === #!/bin/bash make docker-image-centos7 V=1 NETWORK=1 time make docker-test-quick@centos7 SHOW_ENV=1 J=14 NETWORK=1 === TEST SCRIPT END === Not run: 259 Failures: 031 036 061 Failed 3 of 119 iotests make: *** [check-tests/check-block.sh] Error 1 make: *** Waiting for unfinished jobs.... TEST check-qtest-aarch64: tests/qtest/test-hmp TEST check-qtest-aarch64: tests/qtest/qos-test --- raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command '['sudo', '-n', 'docker', 'run', '--label', 'com.qemu.instance.uuid=350f72f6732d405b861f0e9334ef155a', '-u', '1001', '--security-opt', 'seccomp=unconfined', '--rm', '-e', 'TARGET_LIST=', '-e', 'EXTRA_CONFIGURE_OPTS=', '-e', 'V=', '-e', 'J=14', '-e', 'DEBUG=', '-e', 'SHOW_ENV=1', '-e', 'CCACHE_DIR=/var/tmp/ccache', '-v', '/home/patchew/.cache/qemu-docker-ccache:/var/tmp/ccache:z', '-v', '/var/tmp/patchew-tester-tmp-kwr2oe7u/src/docker-src.2020-05-26-14.02.04.28988:/var/tmp/qemu:z,ro', 'qemu:centos7', '/var/tmp/qemu/run', 'test-quick']' returned non-zero exit status 2. filter=--filter=label=com.qemu.instance.uuid=350f72f6732d405b861f0e9334ef155a make[1]: *** [docker-run] Error 1 make[1]: Leaving directory `/var/tmp/patchew-tester-tmp-kwr2oe7u/src' make: *** [docker-run-test-quick@centos7] Error 2 real 14m37.383s user 0m8.950s The full log is available at http://patchew.org/logs/1590504866-679474-1-git-send-email-andrey.shinkevich@virtuozzo.com/testing.docker-quick@centos7/?type=message. --- Email generated automatically by Patchew [https://patchew.org/]. Please send your feedback to patchew-devel@redhat.com
On 5/26/20 9:54 AM, Andrey Shinkevich wrote: > Add dirty bitmap information to QCOW2 metadata dump in qcow2.py script. > The sample output: > > Header extension (Bitmaps): This change to the output is independently useful. However, per patchew, it does cause 'make check' to fail: https://patchew.org/logs/1590504866-679474-1-git-send-email-andrey.shinkevich@virtuozzo.com/testing.docker-quick@centos7/?type=message ... --- /tmp/qemu-test/src/tests/qemu-iotests/031.out 2020-05-26 14:44:51.000000000 +0000 +++ /tmp/qemu-test/build/tests/qemu-iotests/031.out.bad 2020-05-26 18:07:11.753556518 +0000 @@ -24,7 +24,7 @@ refcount_order 4 header_length 72 -Header extension: +Header extension (Unknown): ... Failures: 031 036 061 I think it would be wise to split this into two patches, one that makes _just_ the following change: > @@ -143,30 +267,39 @@ class QcowHeader: > print("%-25s" % f[2], value_str) > print("") > > - def dump_extensions(self): > + def dump_extensions(self, fd): > for ex in self.extensions: > > + print("Header extension (%s):" % self.extension_name(ex.magic)) > + print("%-25s %#x" % ("magic", ex.magic)) > + print("%-25s %d" % ("length", ex.length)) and whatever is needed to support that, plus the changes necessary to the iotests output to keep them passing (hopefully, the 3 tests identified by 'make check' covers all of the existing tests already using qcow2.py), then the second patch adding the rest of this that then gives details about the bitmap contents. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
© 2016 - 2025 Red Hat, Inc.