qemu/tests/test-vmstate.c
<<
>>
Prefs
   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
  33static char temp_file[] = "/tmp/vmst.test.XXXXXX";
  34static 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 */
  39void 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 */
  49static 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
  75static 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
  86static 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
  93static 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
 115static 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
 137static 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
 170typedef 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
 184TestSimple 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
 204static 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
 239uint8_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
 257static void obj_simple_copy(void *target, void *source)
 258{
 259    memcpy(target, source, sizeof(TestSimple));
 260}
 261
 262static 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
 294typedef struct TestStruct {
 295    uint32_t a, b, c, e;
 296    uint64_t d, f;
 297    bool skip_c_e;
 298} TestStruct;
 299
 300static 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
 317static 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
 340static 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
 365static bool test_skip(void *opaque, int version_id)
 366{
 367    TestStruct *t = (TestStruct *)opaque;
 368    return !t->skip_c_e;
 369}
 370
 371static 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
 387static 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
 408static 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
 427static 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
 453static 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
 478typedef struct {
 479    int32_t i;
 480} TestStructTriv;
 481
 482const VMStateDescription vmsd_tst = {
 483    .name = "test/tst",
 484    .version_id = 1,
 485    .minimum_version_id = 1,
 486    .fields = (VMStateField[]) {
 487        VMSTATE_INT32(i, TestStructTriv),
 488        VMSTATE_END_OF_LIST()
 489    }
 490};
 491
 492#define AR_SIZE 4
 493
 494typedef struct {
 495    TestStructTriv *ar[AR_SIZE];
 496} TestArrayOfPtrToStuct;
 497
 498const VMStateDescription vmsd_arps = {
 499    .name = "test/arps",
 500    .version_id = 1,
 501    .minimum_version_id = 1,
 502    .fields = (VMStateField[]) {
 503        VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct,
 504                AR_SIZE, 0, vmsd_tst, TestStructTriv),
 505        VMSTATE_END_OF_LIST()
 506    }
 507};
 508static void test_arr_ptr_str_no0_save(void)
 509{
 510    TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
 511    TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
 512    uint8_t wire_sample[] = {
 513        0x00, 0x00, 0x00, 0x00,
 514        0x00, 0x00, 0x00, 0x01,
 515        0x00, 0x00, 0x00, 0x02,
 516        0x00, 0x00, 0x00, 0x03,
 517        QEMU_VM_EOF
 518    };
 519
 520    save_vmstate(&vmsd_arps, &sample);
 521    compare_vmstate(wire_sample, sizeof(wire_sample));
 522}
 523
 524static void test_arr_ptr_str_no0_load(void)
 525{
 526    TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
 527    TestStructTriv ar[AR_SIZE] = {};
 528    TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
 529    int idx;
 530    uint8_t wire_sample[] = {
 531        0x00, 0x00, 0x00, 0x00,
 532        0x00, 0x00, 0x00, 0x01,
 533        0x00, 0x00, 0x00, 0x02,
 534        0x00, 0x00, 0x00, 0x03,
 535        QEMU_VM_EOF
 536    };
 537
 538    save_buffer(wire_sample, sizeof(wire_sample));
 539    SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
 540                          wire_sample, sizeof(wire_sample)));
 541    for (idx = 0; idx < AR_SIZE; ++idx) {
 542        /* compare the target array ar with the ground truth array ar_gt */
 543        g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
 544    }
 545}
 546
 547int main(int argc, char **argv)
 548{
 549    temp_fd = mkstemp(temp_file);
 550
 551    module_call_init(MODULE_INIT_QOM);
 552
 553    g_test_init(&argc, &argv, NULL);
 554    g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
 555    g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
 556    g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
 557    g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
 558    g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
 559    g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
 560    g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
 561    g_test_add_func("/vmstate/array/ptr/str/no0/save",
 562                    test_arr_ptr_str_no0_save);
 563    g_test_add_func("/vmstate/array/ptr/str/no0/load",
 564                    test_arr_ptr_str_no0_load);
 565    g_test_run();
 566
 567    close(temp_fd);
 568    unlink(temp_file);
 569
 570    return 0;
 571}
 572