QEMU TCG Plugins

本文部分内容参考了中科院PLCT实验室王俊强师兄的讲解视频,下附链接,如有侵权,立即整改!
QEMU线上讨论会-王俊强-QEMU TCG Plugings

文章目录

  • QEMU TCG Plugins
    • QEMU TCG Plugins 简介
    • QEMU TCG Plugins 源码
    • QEMU TCG Plugins 流程
    • New Structs
    • QEMU TCG Plugins API
    • QEMU TCG Plugins 数据收集
      • TB & insn数据收集
      • mem数据收集
    • Run
      • hotblocks
      • hotpages
      • howvec
      • execlog
      • cache
      • 测试性能结果
      • 测试性能结果

QEMU TCG Plugins 简介

  • TCG Plugins是自QEMU 4.2以来的新功能,它提供了在代码上运行指令测试的功能。它们能够对系统发出的每个指令和内存访问进行被动监视,不会修改系统模拟的状态。

  • TCG Plugins可以向Translation、execution以及callback中插入事件。

  • Building

    • configure --enable-plugins
  • QEMU启动

    $QEMU $OTHER_QEMU_ARGS \-plugin contrib/plugin/libhowvec.so,inline=on,count=hint \-plugin contrib/plugin/libhotblocks.so
    
  • Plugin是一种观察者模式,虽然是观察者模式但它是被动的,不会影响到QEMU的整体性

  • 在启动参数中,我们发现qemu的plugin被编译成.so

  • QEMU共提供了7个原始的plugin,这7个plugin的功能都非常简单,甚至可以说没完全实现。它们给出了程序员开发plugin的示范

QEMU TCG Plugins 源码

  • Plugin涉及到的源码还是比较多的,其中功能源码指的是Plugin和QEMU内核的交互代码,增加在这些内核文件中。
  • 接口API将Plugin的使用和内核代码解耦合,更安全。不会暴露QEMU的内核代码。同时为了保证安全性,Plugin还设计了一系列自己的结构体而避免使用QEMU的结构体,这点会在后面讲解。
  • 原始plugin是QEMU Plugins提供的几个示例Plugin的源码,这里面大量使用了接口API和新定义的结构体
  • 头文件主要位于这两个文件夹下面。定义了结构体,函数方法等

QEMU TCG Plugins 流程

  • 首先插件加载后,会调用install函数,install函数会注册相关的回调函数cbs,包括插件处理的事件,插件退出atexit函数,这个函数会在QEMU退出时执行,会释放掉插件处理中收集和产生的数据。

  • 每当事件发生时,事件回调函数就会被调用,在函数中写插件的处理逻辑。我们可以选择处理信息的范围,所有的指令信息都可以被获得。

  • 同时事件回调函数也可以是内联函数,注意内联函数的操作不要太复杂,使用内敛函数时一些数据的一致性则无法保证。

  • 当QEMU退出时,atexit_cb回调函数会被调用。

New Structs

QEMU内核源码应该是对插件模糊的,为了隐藏源码细节,Plugin定义了一套自己的结构体和数据类型,基本都以"qemu_plugin_"开头,包括函数命名。

  • 为了保护内核代码,Plugin提供了新的结构体供编写插件使用。主要分为三个方面

    • qemu_plugin_tb 就是为了替代翻译代码时用到的TB块

    • 指令信息方面

    • Mem信息方面

      • hwaddr是具体的mem的结构体
      • 而meminfo这个只是一个32位的值,它被用来做与或运算来判断mem信息的具体类型,比如store、load、 大小端
  • 回调函数一共分2大类和2小类,大类就是insn相关和mem相关,小类就是一般的回调函数和内联函数

QEMU TCG Plugins API

代码在/plugins/api.c中

/plugins文件夹下还有其他文件,其中:

  • core.c是如何向qemu中注入plugin的代码,包括plugin注册,plugin生成callback方法等
  • loader.c是专门处理plugin的加载和卸载,在移除plugin时保证plugin的代码及生成的代码能够完全清除

TCG Plugins提供了很多写好的api给开发者,程序员可以使用这些api完成一个Plugin的编写。主要分为3大块:

  • Plugin注册相关:

    这些API可以让程序员决定Plugin什么时候起作用,可以是vCPU上触发,也可以是TB块、Instruction和Memory中。

  • Plugin功能逻辑相关:

    我们的Plugin一般都有处理数据的一系列逻辑,如果想从QEMU模拟内核中获取相关数据,就可以使用这一类API。比如获取TB块指令数,获取指令的各种信息等等。

  • Plugin生命周期相关:

    源码提供了5个生命周期相关的API,分别是install、reset、output、exit、uninstall。其中,install和exit需要每个Plugin都实现自己的,其他的可以使用通用的API

QEMU TCG Plugins 数据收集

从上文得知,我们知道了QEMU Plugins的基本运行逻辑,也似乎清楚了如何写一个新的Plugin。下面介绍Plugin和QEMU内核的交互代码,弄清Plugin是如何驱动QEMU生成Plugin需要的数据,即维护了新定义的qemu_plugin_tb等结构体。这样不受源码的拘束,写更强大的Plugin。

在Plugin的生命周期中,“Call qemu_plugin_install”时期定义了Plugin的注册代码和事件回调函数,注册代码我们可以选择是在tb翻译时期、insn生成时期以及mem生成时期。但是Plugin的注册事件只分为:

enum qemu_plugin_event {QEMU_PLUGIN_EV_VCPU_INIT,QEMU_PLUGIN_EV_VCPU_EXIT,QEMU_PLUGIN_EV_VCPU_TB_TRANS,QEMU_PLUGIN_EV_VCPU_IDLE,QEMU_PLUGIN_EV_VCPU_RESUME,QEMU_PLUGIN_EV_VCPU_SYSCALL,QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,QEMU_PLUGIN_EV_FLUSH,QEMU_PLUGIN_EV_ATEXIT,QEMU_PLUGIN_EV_MAX, /* total number of plugin events we support */
};

并不像前面提到的Plugin API中那么多种。qemu_plugin_register_vcpu_tb_trans_cb对应了QEMU_PLUGIN_EV_VCPU_TB_TRANS事件,只有该事件下才会影响到而且QEMU内核代码生成数据,其他的事件都是plugin的内部逻辑代码,而且有的register方法都没有对应的事件,比如vcpu_mem_cb是纯plugin逻辑方法,感觉这也是plugin 函数名设计迷惑人的地方。

TB & insn数据收集

在${QEMU}/accel/tcg/translator.c中,有一个关键方法translator_loop。这个方法控制着TB的翻译

void translator_loop(const TranslatorOps *ops, DisasContextBase *db,CPUState *cpu, TranslationBlock *tb, int max_insns)
{...plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY);while(true) {...if (plugin_enabled) {plugin_gen_insn_start(cpu, db);}...if (plugin_enabled) {plugin_gen_insn_end();}...}...if (plugin_enabled) {plugin_gen_tb_end(cpu);}...
}

我们只关注Plugin相关的,看出Plugin作者模仿TB翻译的流程,在每个TB块翻译的开始和结束分别调用了plugin_gen_tb_start和end,每个指令的翻译都调用了plugin_gen_insn_start和end。这些方法实现在${QEMU}/accel/tcg/plugin-gen.c中,这时Plugin非常核心的代码。通过这几个方法,Plugin就生成完整的qemu_plugin_tb和qemu_plugin_insn结构体数据。

在translator_loop中执行plugin_gen_tb_end之前,tb和insn的信息就已经生成完毕,在plugin_gen_tb_end中会执行tb_trans_cb,它会拿Plugin工具的vcpu_tb_trans来执行,这也就是为什么每个示例Plugin中,作者都实现了vcpu_tb_trans函数,将这个函数作为每个Plugin逻辑的开始。有的工具比较简单,只需要到这个阶段就可以了,而有的工具比较复杂,比如hotpages工具,那么就需要在vcpu_tb_trans中后面其他时期要运行的代码。下面是hotpages的vcpu_tb_trans函数:

static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
{size_t n = qemu_plugin_tb_n_insns(tb);size_t i;for (i = 0; i < n; i++) {struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);qemu_plugin_register_vcpu_mem_cb(insn, vcpu_haddr,QEMU_PLUGIN_CB_NO_REGS,rw, NULL);}
}

mem数据收集

Plugin的mem数据收集比较特殊,只有当使用到mem数据的时候,Plugin才会收集。比如调用qemu_plugin_get_hwaddr方法,此时会查TLB来确定hwaddr的值。当TB翻译被执行时,默认TLB是被填满的。

io_writex可能会造成TLB flush或者TLB的大小调整,导致信息丢失。在这种情况下需要从iotlbentry中恢复数据。但是从源码来看好像并没写恢复数据的代码。

Run

首先编译,本例以qemu-6.1为例

  • 在根目录下(验证的是arm架构)

    ./configure --target-list=aarch64-softmmu --enable-plugins
    

    运行完成后会生成build目录

  • 编译

    cd build
    make -j
    
  • qemu编译完成后需要手动去编译plugin

    cd build/contrib/plugins
    make
    

    这样就可以得到所有plugin的.so文件,该目录也是plugin所在的目录

hotblocks

  • cmd

    ./build/aarch64-softmmu/qemu-system-aarch64  \-machine virt,virtualization=true,gic-version=3 \-nographic \-m size=1024M \-cpu cortex-a57  \-smp cpus=1 \-kernel /yourKernel  \-hda /your.img \-append "root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc"  \-plugin build/contrib/plugins/libhotblocks.so -d plugin \
    
  • 运行结果

    collected 118731 entries in the hash table
    pc, tcount, icount, ecount
    0x000000000047e268, 1, 3, 1902517016
    0x0000ffff9b0c2b00, 1, 4, 869322965
    0x000000000063b630, 1, 3, 675017603
    0x000000000063b63c, 1, 3, 675017603
    0x0000000000405330, 1, 1, 163144534
    0x0000000000405334, 1, 4, 163144534
    0x0000000000405328, 1, 2, 163137397
    0x00000000004053a4, 1, 2, 163137397
    0x00000000004417ac, 1, 1, 150975332
    0x00000000004417b4, 1, 2, 150975332
    0x0000000000441860, 1, 6, 150975332
    0x0000000000441890, 1, 12, 150175916
    0x00000000004417bc, 1, 1, 149083686
    0x0000000000441830, 1, 6, 147212517
    0x00000000004418c0, 1, 5, 146872575
    0x00000000004419ec, 1, 2, 146506598
    0x00000000004419f4, 1, 7, 145575716
    0x00000000004418f0, 1, 7, 144941383
    0x00000000005f85d0, 1, 5, 141259864
    0x00000000005f6b5c, 1, 3, 141259864
    

hotpages

  • cmd

    ./build/aarch64-softmmu/qemu-system-aarch64  \-machine virt,virtualization=true,gic-version=3 \-nographic \-m size=1024M \-cpu cortex-a57  \-smp cpus=1 \-kernel /yourKernel  \-hda /your.img \-append "root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc"  \-plugin build/contrib/plugins/libhotpages.so -d plugin
    
  • 运行结果

    未识别qemu_plugin_hwaddr_phys_addr

howvec

  • cmd

    ./build/aarch64-softmmu/qemu-system-aarch64  \-machine virt,virtualization=true,gic-version=3 \-nographic \-m size=1024M \-cpu cortex-a57  \-kernel /yourKernel  \-hda /your.img \-append "root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc"  \-smp 1 -plugin build/contrib/plugins/libhowvec.so -d plugin
    

howvec plugin官网文档运行命令为"…libhowvec.so,count=sreg -d plugin"但是这样会报错,我查看了源码也没有对count参数的处理,暂且去掉

  • 运行结果

    Instruction Classes:
    Class:   UDEF                   not counted
    Class:   PCrel addr             (2189089904 hits)
    Class:   Add/Sub (imm)          (12016160717 hits)
    Class:   Logical (imm)          (290405307 hits)
    Class:   Move Wide (imm)        (1278700511 hits)
    Class:   Bitfield               (601491132 hits)
    Class:   Extract                (73188 hits)
    Class:   Cond Branch (imm)      (10963119449 hits)
    Class:   Exception Gen          (1877 hits)
    Class:     NOP                  not counted
    Class:   Hints                  (6020 hits)
    Class:   Barriers               (4949456 hits)
    Class:   PSTATE                 (1660531 hits)
    Class:   System Insn            (889210271 hits)
    Class:   System Reg             (19424950 hits)
    Class:   Branch (reg)           (1451181617 hits)
    Class:   Branch (imm)           (2226632283 hits)
    Class:   Cmp & Branch           (2999486060 hits)
    Class:   Tst & Branch           (299176864 hits)
    Class:   ldst excl              (7531814 hits)
    Class:   Load Reg (lit)         (5054690402 hits)
    Class:   ldst noalloc pair      (9049600 hits)
    Class:   ldst pair              (17420022029 hits)
    Class:   ldst reg (imm)         (11669731753 hits)
    Class: Loads & Stores           (176306 hits)
    Class: Data Proc Reg            (3015727186 hits)
    Class: Scalar FP                (11884944 hits)
    

execlog

  • cmd

    ./build/aarch64-softmmu/qemu-system-aarch64  \-machine virt,virtualization=true,gic-version=3 \-nographic \-m size=1024M \-cpu cortex-a57  \-smp cpus=1 \-kernel /yourKernel  \-hda /your.img \-append "root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc"  \-plugin build/contrib/plugins/libexeclog.so -d plugin
    
  • 运行结果

    和hotpages相同

cache

  • cmd

    ./build/aarch64-softmmu/qemu-system-aarch64  \-machine virt,virtualization=true,gic-version=3 \-nographic \-m size=1024M \-cpu cortex-a57  \-smp cpus=1 \-kernel /yourKernel  \-hda /your.img \-append "root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc"  \-plugin build/contrib/plugins/libcache.so -d plugin -D cache.log
    
  • 运行结果

    undefined symbol: qemu_plugin_insn_symbol

测试性能结果

  • 实验参数

    • version: qemu-6.1
    • benchmark: spec2006 403.gcc-1
    • cpu: 1
  • 结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZlUMfSQA-1655289681989)(https://raw.githubusercontent.com/JaCenz/images/master/img/image-20220615181418293.png)]
    l
    -hda /your.img
    -append “root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc”
    -plugin build/contrib/plugins/libcache.so -d plugin -D cache.log

    
    
  • 运行结果

    undefined symbol: qemu_plugin_insn_symbol

测试性能结果

  • 实验参数

    • version: qemu-6.1
    • benchmark: spec2006 403.gcc-1
    • cpu: 1
  • 结果


欢迎讨论~

QEMU TCG Plugins详解相关推荐

  1. OpenStack Queens版搭建详解

    目录 OpenStack Queens版搭建详解 1.基础环境配置 1.2 节点网络规划 1.3 关闭防火墙 1.4 配置yum源 1.5 配置节点IP 1.6 配置主机名 1.7 配置主机名解析(h ...

  2. Libvirt XML文件详解(一)

    Libvirt XML文件详解(一) 1 根元素 2 通用元数据( General metadata ) name uuid genid title description metadata 2 操作 ...

  3. ATS程序功能和使用方法详解

    转载自https://blog.zymlinux.net/index.php/archives/374 Apache Traffic Server的程序文件,与传统的服务器系统有大不同,这里我们将会对 ...

  4. TC配置文件WCMD.INI详解,只能在ini重修改的配置

    有*的项目扩展了功能,有★的项目是只能在INI中修改的配置. ★Allowed=     允许访问哪些驱动器(\代表网络邻居).例如写为Allowed=cde\,代表仅允许访问C.D.E和网络邻居,其 ...

  5. mybatis mysql 配置文件_Mybatis配置文件详解(4)

    本次主要来了解: MyBatis数据库配置文件SqlMapConfig.xml SQL映射配置中输入映射的配置 SQL映射配置中输出映射的配置 SQL映射配置中动态SQL语句的配置 1. SqlMap ...

  6. spring boot 实战 / 可执行war启动参数详解

    概述   上一篇文章<spring boot 实战 / mvn spring-boot:run 参数详解>主要讲解了spring boot 项目基于maven插件启动过程中借助profil ...

  7. elasticsearch-.yml(中文配置详解)

    此elasticsearch-.yml配置文件,是在$ES_HOME/config/下 elasticsearch-.yml(中文配置详解) # ======================== El ...

  8. linux yum命令详解

    yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RP ...

  9. java data使用_@Data注解详解及使用方法-Fun言

    @Data作用 去除Getter,Setter,equals,hashCode,toString方法,@Data注解在类上时,简化java代码编写,为该类提供读写属性.简单来说就是不用再手动添加get ...

最新文章

  1. KindEditor编辑器在ASP.NET中的使用
  2. ELK+Kafka 企业日志收集平台(一)
  3. android多线程文章,Android 多线程处理之多线程用法大集合
  4. 谈谈CTO、技术总监、首席架构师的区别
  5. pop to 特定的UIViewController
  6. 12123两小时没付款怎么办_交管12123支付宝小程序付款不了怎么办 交管12123支付宝缴费方法介绍[多图]...
  7. python查找当前路径,在Python中查找当前终端选项卡的当前目录
  8. 基于mysql的淘宝用户、商品、平台价值分析
  9. 电脑分屏工具 v1.22
  10. 计算机人类的三大科学思维,什么是科学思维:科学思维可以分为理论、实验、计算思维...
  11. c语言实验报告总结通用版,C语言实训心得体会
  12. 从这条博客开始转变!
  13. 马云关于计算机的名人名言,马云名人名言
  14. win10cmd计算机管理界面,win10系统如何CMD中进行电脑关机/重启
  15. docker安装了nacos,浏览器却无法访问到页面
  16. 从两句偈语开始写的一首诗《看花》,以及创造思路、过程
  17. VAE与后验分布、先验分布
  18. 《Effective Modern C++》Item 6: Use the explicitly typed initializer idiom when auto deduces undesired
  19. error LNK2001解决方法
  20. chrome浏览器调试JS代码

热门文章

  1. 解决了:无法加载文件或程序集'stdole, Version=7.0.3300.0'
  2. python计算某个数的阶乘
  3. 【LOJ】 #2547. 「JSOI2018」防御网络
  4. 2022年P气瓶充装上岗证题库模拟考试平台操作
  5. 本题要求实现一个函数,判断任一给定整数N是否满足条件:它是完全平方数,又至少有两位数字相同,如144、676等。
  6. 【Android 2d游戏开发(3)】——贪吃蛇(基于surfaceview框架,精讲)
  7. 汽车毫米波雷达测试解决方案
  8. 华工计算机答辩,华南理工大学学位论文答辩注意事项!
  9. 阿里云oss对象存储上传照片并返回照片路径
  10. GIS之缓冲区分析、叠加分析