]>
git.proxmox.com Git - mirror_qemu.git/blob - scripts/vmstate-static-checker.py
3 # Compares vmstate information stored in JSON format, obtained from
4 # the -dump-vmstate QEMU command.
6 # Copyright 2014 Amit Shah <amit.shah@redhat.com>
7 # Copyright 2014 Red Hat, Inc.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License along
20 # with this program; if not, see <http://www.gnu.org/licenses/>.
26 # Count the number of errors found
32 # Ensure we don't wrap around or reset to 0 -- the shell only has
33 # an 8-bit return value.
38 def check_fields_match ( name
, s_field
, d_field
):
39 if s_field
== d_field
:
42 # Some fields changed names between qemu versions. This list
43 # is used to whitelist such changes in each section / description.
45 'apic' : [ 'timer' , 'timer_expiry' ],
46 'e1000' : [ 'dev' , 'parent_obj' ],
47 'ehci' : [ 'dev' , 'pcidev' ],
48 'I440FX' : [ 'dev' , 'parent_obj' ],
49 'ich9_ahci' : [ 'card' , 'parent_obj' ],
50 'ich9-ahci' : [ 'ahci' , 'ich9_ahci' ],
51 'ioh3420' : [ 'PCIDevice' , 'PCIEDevice' ],
52 'ioh-3240-express-root-port' : [ 'port.br.dev' ,
53 'parent_obj.parent_obj.parent_obj' ,
54 'port.br.dev.exp.aer_log' ,
55 'parent_obj.parent_obj.parent_obj.exp.aer_log' ],
56 'lsiscsi' : [ 'dev' , 'parent_obj' ],
57 'mch' : [ 'd' , 'parent_obj' ],
58 'pci_bridge' : [ 'bridge.dev' , 'parent_obj' , 'bridge.dev.shpc' , 'shpc' ],
59 'pcnet' : [ 'pci_dev' , 'parent_obj' ],
60 'PIIX3' : [ 'pci_irq_levels' , 'pci_irq_levels_vmstate' ],
61 'piix4_pm' : [ 'dev' , 'parent_obj' , 'pci0_status' ,
62 'acpi_pci_hotplug.acpi_pcihp_pci_status[0x0]' ,
63 'pm1a.sts' , 'ar.pm1.evt.sts' , 'pm1a.en' , 'ar.pm1.evt.en' ,
64 'pm1_cnt.cnt' , 'ar.pm1.cnt.cnt' ,
65 'tmr.timer' , 'ar.tmr.timer' ,
66 'tmr.overflow_time' , 'ar.tmr.overflow_time' ,
68 'rtl8139' : [ 'dev' , 'parent_obj' ],
69 'qxl' : [ 'num_surfaces' , 'ssd.num_surfaces' ],
70 'usb-ccid' : [ 'abProtocolDataStructure' , 'abProtocolDataStructure.data' ],
71 'usb-host' : [ 'dev' , 'parent_obj' ],
72 'usb-mouse' : [ 'usb-ptr-queue' , 'HIDPointerEventQueue' ],
73 'usb-tablet' : [ 'usb-ptr-queue' , 'HIDPointerEventQueue' ],
74 'vmware_vga' : [ 'card' , 'parent_obj' ],
75 'vmware_vga_internal' : [ 'depth' , 'new_depth' ],
76 'xhci' : [ 'pci_dev' , 'parent_obj' ],
77 'x3130-upstream' : [ 'PCIDevice' , 'PCIEDevice' ],
78 'xio3130-express-downstream-port' : [ 'port.br.dev' ,
79 'parent_obj.parent_obj.parent_obj' ,
80 'port.br.dev.exp.aer_log' ,
81 'parent_obj.parent_obj.parent_obj.exp.aer_log' ],
82 'xio3130-downstream' : [ 'PCIDevice' , 'PCIEDevice' ],
83 'xio3130-express-upstream-port' : [ 'br.dev' , 'parent_obj.parent_obj' ,
85 'parent_obj.parent_obj.exp.aer_log' ],
88 if not name
in changed_names
:
91 if s_field
in changed_names
[ name
] and d_field
in changed_names
[ name
]:
96 def get_changed_sec_name ( sec
):
97 # Section names can change -- see commit 292b1634 for an example.
99 "ICH9 LPC" : "ICH9-LPC" ,
105 if changes
[ item
] == sec
:
109 def exists_in_substruct ( fields
, item
):
110 # Some QEMU versions moved a few fields inside a substruct. This
111 # kept the on-wire format the same. This function checks if
112 # something got shifted inside a substruct. For example, the
113 # change in commit 1f42d22233b4f3d1a2933ff30e8d6a6d9ee2d08f
115 if not "Description" in fields
:
118 if not "Fields" in fields
[ "Description" ]:
121 substruct_fields
= fields
[ "Description" ][ "Fields" ]
123 if substruct_fields
== []:
126 return check_fields_match ( fields
[ "Description" ][ "name" ],
127 substruct_fields
[ 0 ][ "field" ], item
)
130 def check_fields ( src_fields
, dest_fields
, desc
, sec
):
131 # This function checks for all the fields in a section. If some
132 # fields got embedded into a substruct, this function will also
133 # attempt to check inside the substruct.
135 d_iter
= iter ( dest_fields
)
136 s_iter
= iter ( src_fields
)
138 # Using these lists as stacks to store previous value of s_iter
139 # and d_iter, so that when time comes to exit out of a substruct,
140 # we can go back one level up and continue from where we left off.
151 s_item
= s_iter
. next ()
152 except StopIteration :
153 if s_iter_list
== []:
156 s_iter
= s_iter_list
. pop ()
159 # We want to avoid advancing just once -- when entering a
160 # dest substruct, or when exiting one.
165 d_item
= d_iter
. next ()
166 except StopIteration :
167 if d_iter_list
== []:
168 # We were not in a substruct
169 print "Section \" " + sec
+ " \" ," ,
170 print "Description " + " \" " + desc
+ " \" :" ,
171 print "expected field \" " + s_item
[ "field" ] + " \" ," ,
172 print "while dest has no further fields"
176 d_iter
= d_iter_list
. pop ()
182 if not check_fields_match ( desc
, s_item
[ "field" ], d_item
[ "field" ]):
183 # Some fields were put in substructs, keeping the
184 # on-wire format the same, but breaking static tools
187 # First, check if dest has a new substruct.
188 if exists_in_substruct ( d_item
, s_item
[ "field" ]):
189 # listiterators don't have a prev() function, so we
190 # have to store our current location, descend into the
191 # substruct, and ensure we come out as if nothing
192 # happened when the substruct is over.
194 # Essentially we're opening the substructs that got
195 # added which didn't change the wire format.
196 d_iter_list
. append ( d_iter
)
197 substruct_fields
= d_item
[ "Description" ][ "Fields" ]
198 d_iter
= iter ( substruct_fields
)
202 # Next, check if src has substruct that dest removed
203 # (can happen in backward migration: 2.0 -> 1.5)
204 if exists_in_substruct ( s_item
, d_item
[ "field" ]):
205 s_iter_list
. append ( s_iter
)
206 substruct_fields
= s_item
[ "Description" ][ "Fields" ]
207 s_iter
= iter ( substruct_fields
)
211 print "Section \" " + sec
+ " \" ," ,
212 print "Description \" " + desc
+ " \" :" ,
213 print "expected field \" " + s_item
[ "field" ] + " \" ," ,
214 print "got \" " + d_item
[ "field" ] + " \" ; skipping rest"
218 check_version ( s_item
, d_item
, sec
, desc
)
220 if not "Description" in s_item
:
221 # Check size of this field only if it's not a VMSTRUCT entry
222 check_size ( s_item
, d_item
, sec
, desc
, s_item
[ "field" ])
224 check_description_in_list ( s_item
, d_item
, sec
, desc
)
227 def check_subsections ( src_sub
, dest_sub
, desc
, sec
):
228 for s_item
in src_sub
:
230 for d_item
in dest_sub
:
231 if s_item
[ "name" ] != d_item
[ "name" ]:
235 check_descriptions ( s_item
, d_item
, sec
)
238 print "Section \" " + sec
+ " \" , Description \" " + desc
+ " \" :" ,
239 print "Subsection \" " + s_item
[ "name" ] + " \" not found"
243 def check_description_in_list ( s_item
, d_item
, sec
, desc
):
244 if not "Description" in s_item
:
247 if not "Description" in d_item
:
248 print "Section \" " + sec
+ " \" , Description \" " + desc
+ " \" ," ,
249 print "Field \" " + s_item
[ "field" ] + " \" : missing description"
253 check_descriptions ( s_item
[ "Description" ], d_item
[ "Description" ], sec
)
256 def check_descriptions ( src_desc
, dest_desc
, sec
):
257 check_version ( src_desc
, dest_desc
, sec
, src_desc
[ "name" ])
259 if not check_fields_match ( sec
, src_desc
[ "name" ], dest_desc
[ "name" ]):
260 print "Section \" " + sec
+ " \" :" ,
261 print "Description \" " + src_desc
[ "name" ] + " \" " ,
262 print "missing, got \" " + dest_desc
[ "name" ] + " \" instead; skipping"
267 if not f
in dest_desc
:
268 print "Section \" " + sec
+ " \" " ,
269 print "Description \" " + src_desc
[ "name" ] + " \" :" ,
270 print "Entry \" " + f
+ " \" missing"
275 check_fields ( src_desc
[ f
], dest_desc
[ f
], src_desc
[ "name" ], sec
)
277 if f
== 'Subsections' :
278 check_subsections ( src_desc
[ f
], dest_desc
[ f
], src_desc
[ "name" ], sec
)
281 def check_version ( s
, d
, sec
, desc
= None ):
282 if s
[ "version_id" ] > d
[ "version_id" ]:
283 print "Section \" " + sec
+ " \" " ,
285 print "Description \" " + desc
+ " \" :" ,
286 print "version error:" , s
[ "version_id" ], ">" , d
[ "version_id" ]
289 if not "minimum_version_id" in d
:
292 if s
[ "version_id" ] < d
[ "minimum_version_id" ]:
293 print "Section \" " + sec
+ " \" " ,
295 print "Description \" " + desc
+ " \" :" ,
296 print "minimum version error:" , s
[ "version_id" ], "<" ,
297 print d
[ "minimum_version_id" ]
301 def check_size ( s
, d
, sec
, desc
= None , field
= None ):
302 if s
[ "size" ] != d
[ "size" ]:
303 print "Section \" " + sec
+ " \" " ,
305 print "Description \" " + desc
+ " \" " ,
307 print "Field \" " + field
+ " \" " ,
308 print "size mismatch:" , s
[ "size" ], "," , d
[ "size" ]
312 def check_machine_type ( s
, d
):
313 if s
[ "Name" ] != d
[ "Name" ]:
314 print "Warning: checking incompatible machine types:" ,
315 print " \" " + s
[ "Name" ] + " \" , \" " + d
[ "Name" ] + " \" "
320 help_text
= "Parse JSON-formatted vmstate dumps from QEMU in files SRC and DEST. Checks whether migration from SRC to DEST QEMU versions would break based on the VMSTATE information contained within the JSON outputs. The JSON output is created from a QEMU invocation with the -dump-vmstate parameter and a filename argument to it. Other parameters to QEMU do not matter, except the -M (machine type) parameter."
322 parser
= argparse
. ArgumentParser ( description
= help_text
)
323 parser
. add_argument ( '-s' , '--src' , type = file , required
= True ,
324 help = 'json dump from src qemu' )
325 parser
. add_argument ( '-d' , '--dest' , type = file , required
= True ,
326 help = 'json dump from dest qemu' )
327 parser
. add_argument ( '--reverse' , required
= False , default
= False ,
329 help = 'reverse the direction' )
330 args
= parser
. parse_args ()
332 src_data
= json
. load ( args
. src
)
333 dest_data
= json
. load ( args
. dest
)
344 if not dest_sec
in dest_data
:
345 # Either the section name got changed, or the section
346 # doesn't exist in dest.
347 dest_sec
= get_changed_sec_name ( sec
)
348 if not dest_sec
in dest_data
:
349 print "Section \" " + sec
+ " \" does not exist in dest"
354 d
= dest_data
[ dest_sec
]
356 if sec
== "vmschkmachine" :
357 check_machine_type ( s
, d
)
360 check_version ( s
, d
, sec
)
364 print "Section \" " + sec
+ " \" : Entry \" " + entry
+ " \" " ,
369 if entry
== "Description" :
370 check_descriptions ( s
[ entry
], d
[ entry
], sec
)
375 if __name__
== '__main__' :