qemu单元测试二

qemu单元测试glib的测试框架,其使用可以参考:https://developer.gnome.org/glib/2.37/glib-Testing.html

对于一个用户来说使用这个框架比较简单,下面是一个arm下的tmp105的测试用例:

int main(int argc, char **argv)
{
    QTestState *s = NULL;
    int ret;


    g_test_init(&argc, &argv, NULL);


    s = qtest_start("-display none -machine n800");
    i2c = omap_i2c_create(OMAP2_I2C_1_BASE);
    addr = N8X0_ADDR;


    qtest_add_func("/tmp105/tx-rx", send_and_receive);


    ret = g_test_run();


    if (s) {
        qtest_quit(s);
    }
    g_free(i2c);


    return ret;
}

主要步骤是: 调用g_test_init初始化测试框架, 调用qtest_start启动测试的单板, 创建测试用例并添加到测试框架中,执行测试用例, 退出测试等。

这里面主要的函数是qtest _start,它主要功能是创建qemu的测试环境。这里qemu的执行和测试用例是分开的,两个进程通过socket进行通信。测试进程是客户端,qemu进程是服务器端。在tests/libtest.c:

QTestState *qtest_init(const char *extra_args)
{
    QTestState *s;
    int sock, qmpsock, i;
    gchar *pid_file;
    gchar *command;
    const char *qemu_binary;
    pid_t pid;


    qemu_binary = getenv("QTEST_QEMU_BINARY");
    g_assert(qemu_binary != NULL);


    s = g_malloc(sizeof(*s));


    s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
    s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());


    sock = init_socket(s->socket_path);
    qmpsock = init_socket(s->qmp_socket_path);


    pid = fork();
    if (pid == 0) {
        command = g_strdup_printf("%s "
                                  "-qtest unix:%s,nowait "
                                  "-qtest-log /dev/null "
                                  "-qmp unix:%s,nowait "
                                  "-pidfile %s "
                                  "-machine accel=qtest "
                                  "%s", qemu_binary, s->socket_path,
                                  s->qmp_socket_path, pid_file,
                                  extra_args ?: "");
        execlp("/bin/sh", "sh", "-c", command, NULL);
        exit(1);
    }

    s->fd = socket_accept(sock);
    s->qmp_fd = socket_accept(qmpsock);

    s->rx = g_string_new("");
    s->pid_file = pid_file;
    s->child_pid = pid;
    for (i = 0; i < MAX_IRQ; i++) {
        s->irq_level[i] = false;
    }

    /* Read the QMP greeting and then do the handshake */
    qtest_qmp(s, "");
    qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }");

    if (getenv("QTEST_STOP")) {
        kill(qtest_qemu_pid(s), SIGSTOP);
    }

    return s;
}
这个函数创建了qemu的执行进程,在qemu单元测试的情况下使用的的accel是qtest方式,因为在这个模式下quest并不需要执行指令,但是需要创建整个单板以方便测试。
以一个最简单的writew为例,执行流程如下: 首先测试单元执行这个指令:

static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr)
{
    uint16_t data = addr;


    writew(s->addr + OMAP_I2C_SA, data);
    data = readw(s->addr + OMAP_I2C_SA);
    g_assert_cmphex(data, ==, addr);
}

writew最终会调用qtest_writew:

void qtest_writew(QTestState *s, uint64_t addr, uint16_t value)
{
    qtest_write(s, "writew", addr, value);
}
static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    socket_sendf(s->fd, fmt, ap);
    va_end(ap);
}
qtest_sendf通过socket.c将cmd以及相关的参数传送给qemu进程

    } else if (strcmp(words[0], "writeb") == 0 ||
               strcmp(words[0], "writew") == 0 ||
               strcmp(words[0], "writel") == 0 ||
               strcmp(words[0], "writeq") == 0) {
        uint64_t addr;
        uint64_t value;

        g_assert(words[1] && words[2]);
        addr = strtoull(words[1], NULL, 0);
        value = strtoull(words[2], NULL, 0);

        if (words[0][5] == 'b') {
            uint8_t data = value;
            cpu_physical_memory_write(addr, &data, 1);
        } else if (words[0][5] == 'w') {
            uint16_t data = value;
            tswap16s(&data);
            cpu_physical_memory_write(addr, &data, 2);
        } else if (words[0][5] == 'l') {
            uint32_t data = value;
            tswap32s(&data);
            cpu_physical_memory_write(addr, &data, 4);
        } else if (words[0][5] == 'q') {
            uint64_t data = value;
            tswap64s(&data);
            cpu_physical_memory_write(addr, &data, 8);
        }
        qtest_send_prefix(chr);
        qtest_send(chr, "OK\n");

对write的处理是解析出addr,data,len的参数,然后调用cpu_phsical_memory_write完成这个操作

int qtest_init(void)
{
    CharDriverState *chr;


    g_assert(qtest_chrdev != NULL);
    
    configure_icount("0");
    chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
    
    qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr);
    qemu_chr_fe_set_echo(chr, true);
    
    inbuf = g_string_new("");
        
    if (qtest_log) {
        if (strcmp(qtest_log, "none") != 0) {
            qtest_log_fp = fopen(qtest_log, "w+");
        }
    } else {
        qtest_log_fp = stderr;
    }


    qtest_chr = chr;


    return 0;
}

在测试模式下qemu不会执行任何指令, 只会进行简单的处理。在cpus.c里面qemu初始化的时候选择qemu_dummy_cpu_thread_fn

static void *qemu_dummy_cpu_thread_fn(void *arg)
{
#ifdef _WIN32
    fprintf(stderr, "qtest is not supported under Windows\n");
    exit(1);
#else
    CPUState *cpu = arg; 
    sigset_t waitset;
    int r;

    qemu_mutex_lock_iothread();
    qemu_thread_get_self(cpu->thread);
    cpu->thread_id = qemu_get_thread_id();

    sigemptyset(&waitset);
    sigaddset(&waitset, SIG_IPI);

    /* signal CPU creation */
    cpu->created = true;
    qemu_cond_signal(&qemu_cpu_cond);

    cpu_single_env = cpu->env_ptr;
    while (1) {
        cpu_single_env = NULL;
        qemu_mutex_unlock_iothread();
        do { 
            int sig; 
            r = sigwait(&waitset, &sig);
        } while (r == -1 && (errno == EAGAIN || errno == EINTR));
        if (r == -1) {
            perror("sigwait");
            exit(1);
        }    
        qemu_mutex_lock_iothread();
        cpu_single_env = cpu->env_ptr;
        qemu_wait_io_event_common(cpu);
    }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值