Как заставить QEMU возвращаться только к основному l oop, выполняя по одной инструкции за раз? - PullRequest
0 голосов
/ 27 мая 2020

Я добавил -fPCI к опции компиляции исходного файла QEMU и добавил -shared к последней команде компоновки, так что QEMU стал разделяемой библиотекой, которая может быть загружена динамически. С тех пор я начал пытаться понять QEMU. Я использую dlopen для динамической загрузки qemu и использую dlsym для поиска функций в qemu. Это мой код:

#include<iostream>
#include<dlfcn.h>
#include<stdint.h>

using namespace std;

int main(int argc,char* argv[],char* envp[])
{
    void* handle = dlopen("/home/jack/qemu/qemu-5.0.0/arm-softmmu/libqemu-system-arm.so",RTLD_NOW);
    if(handle == nullptr)
    {
        printf("%s\n",dlerror());
        return 0;
    }

    void    (* qemu_init             )(int,char**,char**);
    void    (* qemu_main_loop        )(void);
    void    (* qemu_cleanup          )(void);
    bool    (* main_loop_should_exit )(void);
    void    (* main_loop_wait        )(int);
    int64_t (* cpu_get_icount        )(void);
    int64_t (* cpu_get_icount_raw    )(void);
    int64_t (* cpu_icount_to_ns      )(int64_t);
    int64_t (* cpu_get_clock         )(void);
    int64_t (* cpu_get_ticks         )(void);

#define GET_SYMBOL_AND_CHECK(X) *((void**)(&X)) = dlsym(handle,#X);if(nullptr == X){printf("lost symbol: "#X"\n");return 0;}
    GET_SYMBOL_AND_CHECK(qemu_init);
    GET_SYMBOL_AND_CHECK(qemu_main_loop);
    GET_SYMBOL_AND_CHECK(qemu_cleanup);
    GET_SYMBOL_AND_CHECK(main_loop_should_exit);
    GET_SYMBOL_AND_CHECK(main_loop_wait);
    GET_SYMBOL_AND_CHECK(cpu_get_icount);
    GET_SYMBOL_AND_CHECK(cpu_get_icount_raw);
    GET_SYMBOL_AND_CHECK(cpu_icount_to_ns);
    GET_SYMBOL_AND_CHECK(cpu_get_clock);
    GET_SYMBOL_AND_CHECK(cpu_get_ticks);
#undef GET_SYMBOL_AND_CHECK

    char* _argv[]=
    {
        "qemu-system-arm",
        "-M",
        "vexpress-a9",
        "-nographic",
        "-kernel",
        "/home/jack/temp/u-boot-2015.01/u-boot",
        "-icount",
        "1",
        "-singlestep",
        "-S",
        "-s",
    };
    int _argc=sizeof(_argv) / sizeof(_argv[0]);

    qemu_init(_argc,_argv,envp);

    while(!main_loop_should_exit())
    {
        main_loop_wait(false);

        //my test code:
        int64_t icount_raw = cpu_get_icount_raw();
        int64_t icount = cpu_get_icount();
        int64_t ticks = cpu_get_ticks();
        int64_t clock = cpu_get_clock();

        printf("----------icount_raw: %jd\n",icount_raw);
        printf("----------icount: %jd\n",icount);
        printf("----------ticks: %jd\n",ticks);
        printf("----------clock: %jd\n",clock);
    }

    qemu_cleanup();
    dlclose(handle);
    return 0;
}

Результат программы следующий:

----------icount_raw: 0
----------icount: 0
----------ticks: 0
----------clock: 27595
----------icount_raw: 0
----------icount: 0
----------ticks: 0
----------clock: 47394


U-Boot 2015.01 (May 25 2020 - 14:42:11)

DRAM:  128 MiB
WARNING: Caches not enabled
Flash: 128 MiB
MMC:   MMC: 0
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   smc911x-0
Warning: smc911x-0 using MAC address from net device

Warning: Your board does not use generic board. Please read
doc/README.generic-board and take action. Boards not
upgraded by the late 2014 may break or be removed.
Hit any key to stop autoboot:  2 ----------icount_raw: 60040125
----------icount: 120271139
----------ticks: 120271139
----------clock: 1001128004
----------icount_raw: 119738560
----------icount: 239668009
----------ticks: 239668009
----------clock: 2002239949
----------icount_raw: 180295711
----------icount: 360782311
----------ticks: 360782311
----------clock: 3003347066
----------icount_raw: 240405702
----------icount: 481002293
----------ticks: 481002293
----------clock: 4004446427
----------icount_raw: 300858002
----------icount: 601906893
----------ticks: 601906893
----------clock: 5005552419
----------icount_raw: 361297422
----------icount: 722785733
----------ticks: 722785733
----------clock: 6006625721
----------icount_raw: 420679210
----------icount: 841549309
----------ticks: 841549309
----------clock: 7007717838
----------icount_raw: 424900860
----------icount: 849992609
----------ticks: 849992609
----------clock: 7082080834
----------icount_raw: 424900883
----------icount: 849992655
----------ticks: 849992655
----------clock: 7082105752
----------icount_raw: 424900906
----------icount: 849992701
----------ticks: 849992701
----------clock: 7082120318
QEMU: Terminated

Я смоделировал основной l oop QEMU и написал его, пока l oop. Я печатаю полученные данные каждый раз в l oop и обнаруживаю, что icount_raw может указывать количество инструкций, выполняемых в данный момент процессором. Что касается других данных, я все еще в замешательстве. Когда эта программа запущена, программа uboot может работать нормально. Я обнаружил, что частота вывода данных на экран составляет примерно раз в секунду. Каждый раз icount_raw сильно увеличивается. Когда я использую gdb для удаленного управления программой для запуска. При использовании команды «si» icount_raw увеличивается на 1 каждый раз, чего я и хочу добиться: каждый раз, когда QEMU выполняет только одну инструкцию, он может вернуться к основному l oop. Я хочу знать, как изменить код QEMU, чтобы каждый раз, когда QEMU выполняет инструкцию, он мог возвращаться к основному l oop вместо использования команды gdb «si». В будущем я хочу знать, как управлять QEMU, чтобы он возвращался к основному l oop после каждого выполнения N инструкций. Я могу свободно установить это N. Я понимаю, что событие QEMU l oop основано на Glib, и я думаю, что моя проблема может потребовать изменения кода, который вызывает Glib в QEMU.

1 Ответ

0 голосов
/ 27 мая 2020

Попытка взять внутренности QEMU и поместить их в DLL - это то, что совершенно не поддерживается, поэтому, боюсь, вы сами пытаетесь выяснить, как исправить ошибки в нем.

В общем, поведение, которое вы описываете, является ожидаемым: QEMU компилирует гостевой код в «блоки трансляции», которые соответствуют множеству гостевых инструкций, а затем также пытается напрямую создавать переходы между этими блоками трансляции, где это возможно. Это важно для производительности: мы не хотим возвращаться к выполнению верхнего уровня l oop чаще, чем это необходимо.

Некоторые из этих оптимизаций управляемы: в восходящем QEMU, -singlestep Параметр командной строки означает «помещать только одну гостевую инструкцию в каждый ТБ», а параметр «-d nochain» означает «не выполнять оптимизацию, связывающую ТБ вместе». В основном это полезно для целей отладки: поведение становится более понятным, а журналы отладки легче читать. Обратной стороной является то, что производительность идет напролом.

...