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 QEMUFile *f; 54 55 lseek(fd, 0, SEEK_SET); 56 if (write) { 57 g_assert_cmpint(ftruncate(fd, 0), ==, 0); 58 } 59 ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); 60 if (write) { 61 f = qemu_fopen_channel_output(ioc); 62 } else { 63 f = qemu_fopen_channel_input(ioc); 64 } 65 object_unref(OBJECT(ioc)); 66 return f; 67 } 68 69 #define SUCCESS(val) \ 70 g_assert_cmpint((val), ==, 0) 71 72 #define FAILURE(val) \ 73 g_assert_cmpint((val), !=, 0) 74 75 static void save_vmstate(const VMStateDescription *desc, void *obj) 76 { 77 QEMUFile *f = open_test_file(true); 78 79 /* Save file with vmstate */ 80 vmstate_save_state(f, desc, obj, NULL); 81 qemu_put_byte(f, QEMU_VM_EOF); 82 g_assert(!qemu_file_get_error(f)); 83 qemu_fclose(f); 84 } 85 86 static void save_buffer(const uint8_t *buf, size_t buf_size) 87 { 88 QEMUFile *fsave = open_test_file(true); 89 qemu_put_buffer(fsave, buf, buf_size); 90 qemu_fclose(fsave); 91 } 92 93 static void compare_vmstate(uint8_t *wire, size_t size) 94 { 95 QEMUFile *f = open_test_file(false); 96 uint8_t result[size]; 97 98 /* read back as binary */ 99 100 g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==, 101 sizeof(result)); 102 g_assert(!qemu_file_get_error(f)); 103 104 /* Compare that what is on the file is the same that what we 105 expected to be there */ 106 SUCCESS(memcmp(result, wire, sizeof(result))); 107 108 /* Must reach EOF */ 109 qemu_get_byte(f); 110 g_assert_cmpint(qemu_file_get_error(f), ==, -EIO); 111 112 qemu_fclose(f); 113 } 114 115 static int load_vmstate_one(const VMStateDescription *desc, void *obj, 116 int version, uint8_t *wire, size_t size) 117 { 118 QEMUFile *f; 119 int ret; 120 121 f = open_test_file(true); 122 qemu_put_buffer(f, wire, size); 123 qemu_fclose(f); 124 125 f = open_test_file(false); 126 ret = vmstate_load_state(f, desc, obj, version); 127 if (ret) { 128 g_assert(qemu_file_get_error(f)); 129 } else{ 130 g_assert(!qemu_file_get_error(f)); 131 } 132 qemu_fclose(f); 133 return ret; 134 } 135 136 137 static int load_vmstate(const VMStateDescription *desc, 138 void *obj, void *obj_clone, 139 void (*obj_copy)(void *, void*), 140 int version, uint8_t *wire, size_t size) 141 { 142 /* We test with zero size */ 143 obj_copy(obj_clone, obj); 144 FAILURE(load_vmstate_one(desc, obj, version, wire, 0)); 145 146 /* Stream ends with QEMU_EOF, so we need at least 3 bytes to be 147 * able to test in the middle */ 148 149 if (size > 3) { 150 151 /* We test with size - 2. We can't test size - 1 due to EOF tricks */ 152 obj_copy(obj, obj_clone); 153 FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2)); 154 155 /* Test with size/2, first half of real state */ 156 obj_copy(obj, obj_clone); 157 FAILURE(load_vmstate_one(desc, obj, version, wire, size/2)); 158 159 /* Test with size/2, second half of real state */ 160 obj_copy(obj, obj_clone); 161 FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2)); 162 163 } 164 obj_copy(obj, obj_clone); 165 return load_vmstate_one(desc, obj, version, wire, size); 166 } 167 168 /* Test struct that we are going to use for our tests */ 169 170 typedef struct TestSimple { 171 bool b_1, b_2; 172 uint8_t u8_1; 173 uint16_t u16_1; 174 uint32_t u32_1; 175 uint64_t u64_1; 176 int8_t i8_1, i8_2; 177 int16_t i16_1, i16_2; 178 int32_t i32_1, i32_2; 179 int64_t i64_1, i64_2; 180 } TestSimple; 181 182 /* Object instantiation, we are going to use it in more than one test */ 183 184 TestSimple obj_simple = { 185 .b_1 = true, 186 .b_2 = false, 187 .u8_1 = 130, 188 .u16_1 = 512, 189 .u32_1 = 70000, 190 .u64_1 = 12121212, 191 .i8_1 = 65, 192 .i8_2 = -65, 193 .i16_1 = 512, 194 .i16_2 = -512, 195 .i32_1 = 70000, 196 .i32_2 = -70000, 197 .i64_1 = 12121212, 198 .i64_2 = -12121212, 199 }; 200 201 /* Description of the values. If you add a primitive type 202 you are expected to add a test here */ 203 204 static const VMStateDescription vmstate_simple_primitive = { 205 .name = "simple/primitive", 206 .version_id = 1, 207 .minimum_version_id = 1, 208 .fields = (VMStateField[]) { 209 VMSTATE_BOOL(b_1, TestSimple), 210 VMSTATE_BOOL(b_2, TestSimple), 211 VMSTATE_UINT8(u8_1, TestSimple), 212 VMSTATE_UINT16(u16_1, TestSimple), 213 VMSTATE_UINT32(u32_1, TestSimple), 214 VMSTATE_UINT64(u64_1, TestSimple), 215 VMSTATE_INT8(i8_1, TestSimple), 216 VMSTATE_INT8(i8_2, TestSimple), 217 VMSTATE_INT16(i16_1, TestSimple), 218 VMSTATE_INT16(i16_2, TestSimple), 219 VMSTATE_INT32(i32_1, TestSimple), 220 VMSTATE_INT32(i32_2, TestSimple), 221 VMSTATE_INT64(i64_1, TestSimple), 222 VMSTATE_INT64(i64_2, TestSimple), 223 VMSTATE_END_OF_LIST() 224 } 225 }; 226 227 /* It describes what goes through the wire. Our tests are basically: 228 229 * save test 230 - save a struct a vmstate to a file 231 - read that file back (binary read, no vmstate) 232 - compare it with what we expect to be on the wire 233 * load test 234 - save to the file what we expect to be on the wire 235 - read struct back with vmstate in a different 236 - compare back with the original struct 237 */ 238 239 uint8_t wire_simple_primitive[] = { 240 /* b_1 */ 0x01, 241 /* b_2 */ 0x00, 242 /* u8_1 */ 0x82, 243 /* u16_1 */ 0x02, 0x00, 244 /* u32_1 */ 0x00, 0x01, 0x11, 0x70, 245 /* u64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, 246 /* i8_1 */ 0x41, 247 /* i8_2 */ 0xbf, 248 /* i16_1 */ 0x02, 0x00, 249 /* i16_2 */ 0xfe, 0x0, 250 /* i32_1 */ 0x00, 0x01, 0x11, 0x70, 251 /* i32_2 */ 0xff, 0xfe, 0xee, 0x90, 252 /* i64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, 253 /* i64_2 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84, 254 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 255 }; 256 257 static void obj_simple_copy(void *target, void *source) 258 { 259 memcpy(target, source, sizeof(TestSimple)); 260 } 261 262 static void test_simple_primitive(void) 263 { 264 TestSimple obj, obj_clone; 265 266 memset(&obj, 0, sizeof(obj)); 267 save_vmstate(&vmstate_simple_primitive, &obj_simple); 268 269 compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive)); 270 271 SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone, 272 obj_simple_copy, 1, wire_simple_primitive, 273 sizeof(wire_simple_primitive))); 274 275 #define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name) 276 277 FIELD_EQUAL(b_1); 278 FIELD_EQUAL(b_2); 279 FIELD_EQUAL(u8_1); 280 FIELD_EQUAL(u16_1); 281 FIELD_EQUAL(u32_1); 282 FIELD_EQUAL(u64_1); 283 FIELD_EQUAL(i8_1); 284 FIELD_EQUAL(i8_2); 285 FIELD_EQUAL(i16_1); 286 FIELD_EQUAL(i16_2); 287 FIELD_EQUAL(i32_1); 288 FIELD_EQUAL(i32_2); 289 FIELD_EQUAL(i64_1); 290 FIELD_EQUAL(i64_2); 291 } 292 #undef FIELD_EQUAL 293 294 typedef struct TestStruct { 295 uint32_t a, b, c, e; 296 uint64_t d, f; 297 bool skip_c_e; 298 } TestStruct; 299 300 static const VMStateDescription vmstate_versioned = { 301 .name = "test/versioned", 302 .version_id = 2, 303 .minimum_version_id = 1, 304 .fields = (VMStateField[]) { 305 VMSTATE_UINT32(a, TestStruct), 306 VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so 307 * we catch bugs more easily. 308 */ 309 VMSTATE_UINT32(c, TestStruct), 310 VMSTATE_UINT64(d, TestStruct), 311 VMSTATE_UINT32_V(e, TestStruct, 2), 312 VMSTATE_UINT64_V(f, TestStruct, 2), 313 VMSTATE_END_OF_LIST() 314 } 315 }; 316 317 static void test_load_v1(void) 318 { 319 uint8_t buf[] = { 320 0, 0, 0, 10, /* a */ 321 0, 0, 0, 30, /* c */ 322 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 323 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 324 }; 325 save_buffer(buf, sizeof(buf)); 326 327 QEMUFile *loading = open_test_file(false); 328 TestStruct obj = { .b = 200, .e = 500, .f = 600 }; 329 vmstate_load_state(loading, &vmstate_versioned, &obj, 1); 330 g_assert(!qemu_file_get_error(loading)); 331 g_assert_cmpint(obj.a, ==, 10); 332 g_assert_cmpint(obj.b, ==, 200); 333 g_assert_cmpint(obj.c, ==, 30); 334 g_assert_cmpint(obj.d, ==, 40); 335 g_assert_cmpint(obj.e, ==, 500); 336 g_assert_cmpint(obj.f, ==, 600); 337 qemu_fclose(loading); 338 } 339 340 static void test_load_v2(void) 341 { 342 uint8_t buf[] = { 343 0, 0, 0, 10, /* a */ 344 0, 0, 0, 20, /* b */ 345 0, 0, 0, 30, /* c */ 346 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 347 0, 0, 0, 50, /* e */ 348 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 349 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 350 }; 351 save_buffer(buf, sizeof(buf)); 352 353 QEMUFile *loading = open_test_file(false); 354 TestStruct obj; 355 vmstate_load_state(loading, &vmstate_versioned, &obj, 2); 356 g_assert_cmpint(obj.a, ==, 10); 357 g_assert_cmpint(obj.b, ==, 20); 358 g_assert_cmpint(obj.c, ==, 30); 359 g_assert_cmpint(obj.d, ==, 40); 360 g_assert_cmpint(obj.e, ==, 50); 361 g_assert_cmpint(obj.f, ==, 60); 362 qemu_fclose(loading); 363 } 364 365 static bool test_skip(void *opaque, int version_id) 366 { 367 TestStruct *t = (TestStruct *)opaque; 368 return !t->skip_c_e; 369 } 370 371 static const VMStateDescription vmstate_skipping = { 372 .name = "test/skip", 373 .version_id = 2, 374 .minimum_version_id = 1, 375 .fields = (VMStateField[]) { 376 VMSTATE_UINT32(a, TestStruct), 377 VMSTATE_UINT32(b, TestStruct), 378 VMSTATE_UINT32_TEST(c, TestStruct, test_skip), 379 VMSTATE_UINT64(d, TestStruct), 380 VMSTATE_UINT32_TEST(e, TestStruct, test_skip), 381 VMSTATE_UINT64_V(f, TestStruct, 2), 382 VMSTATE_END_OF_LIST() 383 } 384 }; 385 386 387 static void test_save_noskip(void) 388 { 389 QEMUFile *fsave = open_test_file(true); 390 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, 391 .skip_c_e = false }; 392 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); 393 g_assert(!qemu_file_get_error(fsave)); 394 395 uint8_t expected[] = { 396 0, 0, 0, 1, /* a */ 397 0, 0, 0, 2, /* b */ 398 0, 0, 0, 3, /* c */ 399 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 400 0, 0, 0, 5, /* e */ 401 0, 0, 0, 0, 0, 0, 0, 6, /* f */ 402 }; 403 404 qemu_fclose(fsave); 405 compare_vmstate(expected, sizeof(expected)); 406 } 407 408 static void test_save_skip(void) 409 { 410 QEMUFile *fsave = open_test_file(true); 411 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, 412 .skip_c_e = true }; 413 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); 414 g_assert(!qemu_file_get_error(fsave)); 415 416 uint8_t expected[] = { 417 0, 0, 0, 1, /* a */ 418 0, 0, 0, 2, /* b */ 419 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 420 0, 0, 0, 0, 0, 0, 0, 6, /* f */ 421 }; 422 423 qemu_fclose(fsave); 424 compare_vmstate(expected, sizeof(expected)); 425 } 426 427 static void test_load_noskip(void) 428 { 429 uint8_t buf[] = { 430 0, 0, 0, 10, /* a */ 431 0, 0, 0, 20, /* b */ 432 0, 0, 0, 30, /* c */ 433 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 434 0, 0, 0, 50, /* e */ 435 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 436 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 437 }; 438 save_buffer(buf, sizeof(buf)); 439 440 QEMUFile *loading = open_test_file(false); 441 TestStruct obj = { .skip_c_e = false }; 442 vmstate_load_state(loading, &vmstate_skipping, &obj, 2); 443 g_assert(!qemu_file_get_error(loading)); 444 g_assert_cmpint(obj.a, ==, 10); 445 g_assert_cmpint(obj.b, ==, 20); 446 g_assert_cmpint(obj.c, ==, 30); 447 g_assert_cmpint(obj.d, ==, 40); 448 g_assert_cmpint(obj.e, ==, 50); 449 g_assert_cmpint(obj.f, ==, 60); 450 qemu_fclose(loading); 451 } 452 453 static void test_load_skip(void) 454 { 455 uint8_t buf[] = { 456 0, 0, 0, 10, /* a */ 457 0, 0, 0, 20, /* b */ 458 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 459 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 460 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 461 }; 462 save_buffer(buf, sizeof(buf)); 463 464 QEMUFile *loading = open_test_file(false); 465 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; 466 vmstate_load_state(loading, &vmstate_skipping, &obj, 2); 467 g_assert(!qemu_file_get_error(loading)); 468 g_assert_cmpint(obj.a, ==, 10); 469 g_assert_cmpint(obj.b, ==, 20); 470 g_assert_cmpint(obj.c, ==, 300); 471 g_assert_cmpint(obj.d, ==, 40); 472 g_assert_cmpint(obj.e, ==, 500); 473 g_assert_cmpint(obj.f, ==, 60); 474 qemu_fclose(loading); 475 } 476 477 int main(int argc, char **argv) 478 { 479 temp_fd = mkstemp(temp_file); 480 481 module_call_init(MODULE_INIT_QOM); 482 483 g_test_init(&argc, &argv, NULL); 484 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); 485 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); 486 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); 487 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); 488 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); 489 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); 490 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); 491 g_test_run(); 492 493 close(temp_fd); 494 unlink(temp_file); 495 496 return 0; 497 } 498