1 /* 2 * Test code for VMState 3 * 4 * Copyright (c) 2013 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 27 #include "qemu-common.h" 28 #include "migration/migration.h" 29 #include "migration/vmstate.h" 30 #include "qemu/coroutine.h" 31 #include "io/channel-file.h" 32 33 static char temp_file[] = "/tmp/vmst.test.XXXXXX"; 34 static int temp_fd; 35 36 /* Fake yield_until_fd_readable() implementation so we don't have to pull the 37 * coroutine code as dependency. 38 */ 39 void yield_until_fd_readable(int fd) 40 { 41 fd_set fds; 42 FD_ZERO(&fds); 43 FD_SET(fd, &fds); 44 select(fd + 1, &fds, NULL, NULL, NULL); 45 } 46 47 48 /* Duplicate temp_fd and seek to the beginning of the file */ 49 static QEMUFile *open_test_file(bool write) 50 { 51 int fd = dup(temp_fd); 52 QIOChannel *ioc; 53 lseek(fd, 0, SEEK_SET); 54 if (write) { 55 g_assert_cmpint(ftruncate(fd, 0), ==, 0); 56 } 57 ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); 58 if (write) { 59 return qemu_fopen_channel_output(ioc); 60 } else { 61 return qemu_fopen_channel_input(ioc); 62 } 63 } 64 65 #define SUCCESS(val) \ 66 g_assert_cmpint((val), ==, 0) 67 68 #define FAILURE(val) \ 69 g_assert_cmpint((val), !=, 0) 70 71 static void save_vmstate(const VMStateDescription *desc, void *obj) 72 { 73 QEMUFile *f = open_test_file(true); 74 75 /* Save file with vmstate */ 76 vmstate_save_state(f, desc, obj, NULL); 77 qemu_put_byte(f, QEMU_VM_EOF); 78 g_assert(!qemu_file_get_error(f)); 79 qemu_fclose(f); 80 } 81 82 static void compare_vmstate(uint8_t *wire, size_t size) 83 { 84 QEMUFile *f = open_test_file(false); 85 uint8_t result[size]; 86 87 /* read back as binary */ 88 89 g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==, 90 sizeof(result)); 91 g_assert(!qemu_file_get_error(f)); 92 93 /* Compare that what is on the file is the same that what we 94 expected to be there */ 95 SUCCESS(memcmp(result, wire, sizeof(result))); 96 97 /* Must reach EOF */ 98 qemu_get_byte(f); 99 g_assert_cmpint(qemu_file_get_error(f), ==, -EIO); 100 101 qemu_fclose(f); 102 } 103 104 static int load_vmstate_one(const VMStateDescription *desc, void *obj, 105 int version, uint8_t *wire, size_t size) 106 { 107 QEMUFile *f; 108 int ret; 109 110 f = open_test_file(true); 111 qemu_put_buffer(f, wire, size); 112 qemu_fclose(f); 113 114 f = open_test_file(false); 115 ret = vmstate_load_state(f, desc, obj, version); 116 if (ret) { 117 g_assert(qemu_file_get_error(f)); 118 } else{ 119 g_assert(!qemu_file_get_error(f)); 120 } 121 qemu_fclose(f); 122 return ret; 123 } 124 125 126 static int load_vmstate(const VMStateDescription *desc, 127 void *obj, void *obj_clone, 128 void (*obj_copy)(void *, void*), 129 int version, uint8_t *wire, size_t size) 130 { 131 /* We test with zero size */ 132 obj_copy(obj_clone, obj); 133 FAILURE(load_vmstate_one(desc, obj, version, wire, 0)); 134 135 /* Stream ends with QEMU_EOF, so we need at least 3 bytes to be 136 * able to test in the middle */ 137 138 if (size > 3) { 139 140 /* We test with size - 2. We can't test size - 1 due to EOF tricks */ 141 obj_copy(obj, obj_clone); 142 FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2)); 143 144 /* Test with size/2, first half of real state */ 145 obj_copy(obj, obj_clone); 146 FAILURE(load_vmstate_one(desc, obj, version, wire, size/2)); 147 148 /* Test with size/2, second half of real state */ 149 obj_copy(obj, obj_clone); 150 FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2)); 151 152 } 153 obj_copy(obj, obj_clone); 154 return load_vmstate_one(desc, obj, version, wire, size); 155 } 156 157 /* Test struct that we are going to use for our tests */ 158 159 typedef struct TestSimple { 160 bool b_1, b_2; 161 uint8_t u8_1; 162 uint16_t u16_1; 163 uint32_t u32_1; 164 uint64_t u64_1; 165 int8_t i8_1, i8_2; 166 int16_t i16_1, i16_2; 167 int32_t i32_1, i32_2; 168 int64_t i64_1, i64_2; 169 } TestSimple; 170 171 /* Object instantiation, we are going to use it in more than one test */ 172 173 TestSimple obj_simple = { 174 .b_1 = true, 175 .b_2 = false, 176 .u8_1 = 130, 177 .u16_1 = 512, 178 .u32_1 = 70000, 179 .u64_1 = 12121212, 180 .i8_1 = 65, 181 .i8_2 = -65, 182 .i16_1 = 512, 183 .i16_2 = -512, 184 .i32_1 = 70000, 185 .i32_2 = -70000, 186 .i64_1 = 12121212, 187 .i64_2 = -12121212, 188 }; 189 190 /* Description of the values. If you add a primitive type 191 you are expected to add a test here */ 192 193 static const VMStateDescription vmstate_simple_primitive = { 194 .name = "simple/primitive", 195 .version_id = 1, 196 .minimum_version_id = 1, 197 .fields = (VMStateField[]) { 198 VMSTATE_BOOL(b_1, TestSimple), 199 VMSTATE_BOOL(b_2, TestSimple), 200 VMSTATE_UINT8(u8_1, TestSimple), 201 VMSTATE_UINT16(u16_1, TestSimple), 202 VMSTATE_UINT32(u32_1, TestSimple), 203 VMSTATE_UINT64(u64_1, TestSimple), 204 VMSTATE_INT8(i8_1, TestSimple), 205 VMSTATE_INT8(i8_2, TestSimple), 206 VMSTATE_INT16(i16_1, TestSimple), 207 VMSTATE_INT16(i16_2, TestSimple), 208 VMSTATE_INT32(i32_1, TestSimple), 209 VMSTATE_INT32(i32_2, TestSimple), 210 VMSTATE_INT64(i64_1, TestSimple), 211 VMSTATE_INT64(i64_2, TestSimple), 212 VMSTATE_END_OF_LIST() 213 } 214 }; 215 216 /* It describes what goes through the wire. Our tests are basically: 217 218 * save test 219 - save a struct a vmstate to a file 220 - read that file back (binary read, no vmstate) 221 - compare it with what we expect to be on the wire 222 * load test 223 - save to the file what we expect to be on the wire 224 - read struct back with vmstate in a different 225 - compare back with the original struct 226 */ 227 228 uint8_t wire_simple_primitive[] = { 229 /* b_1 */ 0x01, 230 /* b_2 */ 0x00, 231 /* u8_1 */ 0x82, 232 /* u16_1 */ 0x02, 0x00, 233 /* u32_1 */ 0x00, 0x01, 0x11, 0x70, 234 /* u64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, 235 /* i8_1 */ 0x41, 236 /* i8_2 */ 0xbf, 237 /* i16_1 */ 0x02, 0x00, 238 /* i16_2 */ 0xfe, 0x0, 239 /* i32_1 */ 0x00, 0x01, 0x11, 0x70, 240 /* i32_2 */ 0xff, 0xfe, 0xee, 0x90, 241 /* i64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, 242 /* i64_2 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84, 243 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 244 }; 245 246 static void obj_simple_copy(void *target, void *source) 247 { 248 memcpy(target, source, sizeof(TestSimple)); 249 } 250 251 static void test_simple_primitive(void) 252 { 253 TestSimple obj, obj_clone; 254 255 memset(&obj, 0, sizeof(obj)); 256 save_vmstate(&vmstate_simple_primitive, &obj_simple); 257 258 compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive)); 259 260 SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone, 261 obj_simple_copy, 1, wire_simple_primitive, 262 sizeof(wire_simple_primitive))); 263 264 #define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name) 265 266 FIELD_EQUAL(b_1); 267 FIELD_EQUAL(b_2); 268 FIELD_EQUAL(u8_1); 269 FIELD_EQUAL(u16_1); 270 FIELD_EQUAL(u32_1); 271 FIELD_EQUAL(u64_1); 272 FIELD_EQUAL(i8_1); 273 FIELD_EQUAL(i8_2); 274 FIELD_EQUAL(i16_1); 275 FIELD_EQUAL(i16_2); 276 FIELD_EQUAL(i32_1); 277 FIELD_EQUAL(i32_2); 278 FIELD_EQUAL(i64_1); 279 FIELD_EQUAL(i64_2); 280 } 281 #undef FIELD_EQUAL 282 283 typedef struct TestStruct { 284 uint32_t a, b, c, e; 285 uint64_t d, f; 286 bool skip_c_e; 287 } TestStruct; 288 289 static const VMStateDescription vmstate_versioned = { 290 .name = "test/versioned", 291 .version_id = 2, 292 .minimum_version_id = 1, 293 .fields = (VMStateField[]) { 294 VMSTATE_UINT32(a, TestStruct), 295 VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so 296 * we catch bugs more easily. 297 */ 298 VMSTATE_UINT32(c, TestStruct), 299 VMSTATE_UINT64(d, TestStruct), 300 VMSTATE_UINT32_V(e, TestStruct, 2), 301 VMSTATE_UINT64_V(f, TestStruct, 2), 302 VMSTATE_END_OF_LIST() 303 } 304 }; 305 306 static void test_load_v1(void) 307 { 308 QEMUFile *fsave = open_test_file(true); 309 uint8_t buf[] = { 310 0, 0, 0, 10, /* a */ 311 0, 0, 0, 30, /* c */ 312 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 313 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 314 }; 315 qemu_put_buffer(fsave, buf, sizeof(buf)); 316 qemu_fclose(fsave); 317 318 QEMUFile *loading = open_test_file(false); 319 TestStruct obj = { .b = 200, .e = 500, .f = 600 }; 320 vmstate_load_state(loading, &vmstate_versioned, &obj, 1); 321 g_assert(!qemu_file_get_error(loading)); 322 g_assert_cmpint(obj.a, ==, 10); 323 g_assert_cmpint(obj.b, ==, 200); 324 g_assert_cmpint(obj.c, ==, 30); 325 g_assert_cmpint(obj.d, ==, 40); 326 g_assert_cmpint(obj.e, ==, 500); 327 g_assert_cmpint(obj.f, ==, 600); 328 qemu_fclose(loading); 329 } 330 331 static void test_load_v2(void) 332 { 333 QEMUFile *fsave = open_test_file(true); 334 uint8_t buf[] = { 335 0, 0, 0, 10, /* a */ 336 0, 0, 0, 20, /* b */ 337 0, 0, 0, 30, /* c */ 338 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 339 0, 0, 0, 50, /* e */ 340 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 341 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 342 }; 343 qemu_put_buffer(fsave, buf, sizeof(buf)); 344 qemu_fclose(fsave); 345 346 QEMUFile *loading = open_test_file(false); 347 TestStruct obj; 348 vmstate_load_state(loading, &vmstate_versioned, &obj, 2); 349 g_assert_cmpint(obj.a, ==, 10); 350 g_assert_cmpint(obj.b, ==, 20); 351 g_assert_cmpint(obj.c, ==, 30); 352 g_assert_cmpint(obj.d, ==, 40); 353 g_assert_cmpint(obj.e, ==, 50); 354 g_assert_cmpint(obj.f, ==, 60); 355 qemu_fclose(loading); 356 } 357 358 static bool test_skip(void *opaque, int version_id) 359 { 360 TestStruct *t = (TestStruct *)opaque; 361 return !t->skip_c_e; 362 } 363 364 static const VMStateDescription vmstate_skipping = { 365 .name = "test/skip", 366 .version_id = 2, 367 .minimum_version_id = 1, 368 .fields = (VMStateField[]) { 369 VMSTATE_UINT32(a, TestStruct), 370 VMSTATE_UINT32(b, TestStruct), 371 VMSTATE_UINT32_TEST(c, TestStruct, test_skip), 372 VMSTATE_UINT64(d, TestStruct), 373 VMSTATE_UINT32_TEST(e, TestStruct, test_skip), 374 VMSTATE_UINT64_V(f, TestStruct, 2), 375 VMSTATE_END_OF_LIST() 376 } 377 }; 378 379 380 static void test_save_noskip(void) 381 { 382 QEMUFile *fsave = open_test_file(true); 383 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, 384 .skip_c_e = false }; 385 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); 386 g_assert(!qemu_file_get_error(fsave)); 387 388 uint8_t expected[] = { 389 0, 0, 0, 1, /* a */ 390 0, 0, 0, 2, /* b */ 391 0, 0, 0, 3, /* c */ 392 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 393 0, 0, 0, 5, /* e */ 394 0, 0, 0, 0, 0, 0, 0, 6, /* f */ 395 }; 396 397 qemu_fclose(fsave); 398 compare_vmstate(expected, sizeof(expected)); 399 } 400 401 static void test_save_skip(void) 402 { 403 QEMUFile *fsave = open_test_file(true); 404 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, 405 .skip_c_e = true }; 406 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); 407 g_assert(!qemu_file_get_error(fsave)); 408 409 uint8_t expected[] = { 410 0, 0, 0, 1, /* a */ 411 0, 0, 0, 2, /* b */ 412 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 413 0, 0, 0, 0, 0, 0, 0, 6, /* f */ 414 }; 415 416 qemu_fclose(fsave); 417 compare_vmstate(expected, sizeof(expected)); 418 } 419 420 static void test_load_noskip(void) 421 { 422 QEMUFile *fsave = open_test_file(true); 423 uint8_t buf[] = { 424 0, 0, 0, 10, /* a */ 425 0, 0, 0, 20, /* b */ 426 0, 0, 0, 30, /* c */ 427 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 428 0, 0, 0, 50, /* e */ 429 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 430 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 431 }; 432 qemu_put_buffer(fsave, buf, sizeof(buf)); 433 qemu_fclose(fsave); 434 435 QEMUFile *loading = open_test_file(false); 436 TestStruct obj = { .skip_c_e = false }; 437 vmstate_load_state(loading, &vmstate_skipping, &obj, 2); 438 g_assert(!qemu_file_get_error(loading)); 439 g_assert_cmpint(obj.a, ==, 10); 440 g_assert_cmpint(obj.b, ==, 20); 441 g_assert_cmpint(obj.c, ==, 30); 442 g_assert_cmpint(obj.d, ==, 40); 443 g_assert_cmpint(obj.e, ==, 50); 444 g_assert_cmpint(obj.f, ==, 60); 445 qemu_fclose(loading); 446 } 447 448 static void test_load_skip(void) 449 { 450 QEMUFile *fsave = open_test_file(true); 451 uint8_t buf[] = { 452 0, 0, 0, 10, /* a */ 453 0, 0, 0, 20, /* b */ 454 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 455 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 456 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 457 }; 458 qemu_put_buffer(fsave, buf, sizeof(buf)); 459 qemu_fclose(fsave); 460 461 QEMUFile *loading = open_test_file(false); 462 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; 463 vmstate_load_state(loading, &vmstate_skipping, &obj, 2); 464 g_assert(!qemu_file_get_error(loading)); 465 g_assert_cmpint(obj.a, ==, 10); 466 g_assert_cmpint(obj.b, ==, 20); 467 g_assert_cmpint(obj.c, ==, 300); 468 g_assert_cmpint(obj.d, ==, 40); 469 g_assert_cmpint(obj.e, ==, 500); 470 g_assert_cmpint(obj.f, ==, 60); 471 qemu_fclose(loading); 472 } 473 474 int main(int argc, char **argv) 475 { 476 temp_fd = mkstemp(temp_file); 477 478 module_call_init(MODULE_INIT_QOM); 479 480 g_test_init(&argc, &argv, NULL); 481 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); 482 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); 483 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); 484 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); 485 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); 486 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); 487 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); 488 g_test_run(); 489 490 close(temp_fd); 491 unlink(temp_file); 492 493 return 0; 494 } 495