前言
花了很多时间,遇到了N多错误,才学会使用M5ops的,呜呜呜呜呜,太菜了。
写这一篇教程,帮助和我遇到同样问题的小伙伴吧~
参考资料
https://www.gem5.org/documentation/general_docs/m5ops/
正文
-
为什么要使用M5ops?
具体介绍上面的参考资料就是官网教程,可以自己去看。
我理解的是,一段程序中,你想要对某几行代码进行详细分析,所以不需要从头开始统计,此时就需要用到M5ops的功能。 -
准备M5ops的环境
需要准备一些库文件啥的,便于后面的编辑链接。先到gem5目录下,运行下面指令:
cd util/m5
scons build/{TARGET_ISA}/out/m5
第二条的TARGET_ISA是下面选项,注意都是小写,没有大写:
- x86
- arm (arm-linux-gnueabihf-gcc)
- thumb (arm-linux-gnueabihf-gcc)
- sparc (sparc64-linux-gnu-gcc)
- arm64 (aarch64-linux-gnu-gcc)
- riscv (riscv64-unknown-linux-gnu-gcc)
比如我的是x86架构,那第二条指令就是scons build/x86/out/m5.
开始使用
如何使用M5ops呢?下面以一个举一个栗子,帮助大家理解整个过程~
-
设置gem5
使用M5ops的前提是正确安装配置了gem5,这应该不用多说了吧~ -
编写测试程序
这里的测试程序可以是c程序,c++程序,java程序。下面我以c语言为例,建立一个test_m5ops.c文件,代码如下:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
// M5op header file
#include "gem5/m5ops.h"
int main() {
printf("Starting simulation\n");
// Mark the simulation start
m5_reset_stats(0, 0);
// Your simulation code here
for (int i = 0; i < 10; i++) {
printf("Iteration %d\n", i);
}
// Mark the simulation end
m5_dump_stats(0, 0);
// Exit the simulation
m5_exit(0);
return 0;
}
上面这段程序有几个注意点:
- 要引入m5ops.h文件,此文件在gem5目录下的include文件夹中
- 调用函数,统计要测试的代码。上面的代码中,我使用了m5_reset_stats(0, 0)和m5_dump_stats(0, 0)函数包围了一段for循环,这表示我需要统计这段for循环的信息。当然还可以用其他函数,比如下面这些函数:
这些函数都是 gem5 中 M5 操作的接口,提供了对仿真过程的控制和管理。下面是每个函数的功能解释:
void m5_arm(uint64_t address);
作用:设置一个触发器,使得当仿真到达指定的 address 地址时触发一个事件。这可以用于在特定地址处进行检查或记录状态。
void m5_quiesce(void);作用:使仿真暂停,直到系统处于静止状态。这通常用于在进行快照或检查系统状态之前,确保系统达到稳定状态。
void m5_quiesce_ns(uint64_t ns);作用:使仿真暂停指定的纳秒数(ns),然后继续。这可以用来确保系统在给定时间内稳定。
void m5_quiesce_cycle(uint64_t cycles);作用:使仿真暂停指定的周期数(cycles),然后继续。这对于需要精确控制的仿真场景很有用。
uint64_t m5_quiesce_time(void);作用:返回系统静止状态时的当前时间戳。这对于记录系统稳定时间点很有帮助。
uint64_t m5_rpns();作用:返回系统的当前仿真时间,以纳秒为单位。
void m5_wake_cpu(uint64_t cpuid);作用:唤醒指定的 CPU 进行处理。cpuid 是要唤醒的 CPU 的标识符。
void m5_exit(uint64_tns_delay);作用:使仿真退出,并在退出前延迟指定的纳秒数(ns_delay)。这个函数通常用于在仿真结束前进行清理工作。
void m5_fail(uint64_t ns_delay, uint64_t code);作用:使仿真失败,并在失败前延迟指定的纳秒数(ns_delay)。code 是失败的错误代码。
unsigned m5_sum(unsigned a, unsigned b, unsigned c, unsigned d, unsigned e,
unsigned f);作用:对六个参数进行简单的加法和检验,通常用于 sanity check(完整性检查),确保 M5 操作接口的正确性。
uint64_t m5_init_param(uint64_t key_str1, uint64_t key_str2);作用:初始化仿真参数。key_str1 和 key_str2 是用于参数初始化的值。
void m5_checkpoint(uint64_tns_delay, uint64_t ns_period);作用:创建仿真快照,在延迟 ns_delay 后进行快照,并在 ns_period 时间间隔后创建进一步的快照。
void m5_reset_stats(uint64_t ns_delay, uint64_t ns_period);
作用:重置统计信息,在延迟 ns_delay 后开始重置,并在 ns_period 时间间隔后继续。
void m5_dump_stats(uint64_t ns_delay, uint64_t ns_period);作用:导出当前统计信息,在延迟 ns_delay 后进行,并在 ns_period 时间间隔后进行更多导出。
void m5_dump_reset_stats(uint64_t ns_delay, uint64_t ns_period);作用:导出并重置统计信息,在延迟 ns_delay 后进行,随后在 ns_period 时间间隔后继续。
uint64_t m5_read_file(void *buffer, uint64_t len, uint64_t offset);作用:从文件中读取数据到 buffer,读取长度为 len 字节,从 offset 位置开始。
uint64_t m5_write_file(void *buffer, uint64_t len, uint64_t offset, const char
*filename);作用:将数据从 buffer 写入到指定的文件 filename,写入长度为 len 字节,从 offset 位置开始。
void m5_debug_break(void);作用:在仿真中插入一个调试断点,便于调试和检查。
void m5_switch_cpu(void);作用:切换当前活动的 CPU。这可能用于多核仿真中的 CPU 切换。
void m5_dist_toggle_sync(void);作用:在分布式仿真中切换同步状态,控制仿真中不同节点的同步行为。 void m5_add_symbol(uint64_t addr,const char *symbol);
作用:在指定地址 addr 添加符号 symbol,用于调试和符号跟踪。
void m5_load_symbol(void);作用:加载符号信息,用于符号跟踪和调试。
void m5_panic(void);作用:触发系统的紧急停止(panic),用于处理严重错误情况。
void m5_work_begin(uint64_t workid,uint64_t threadid);作用:标记工作线程的开始,workid 是工作标识符,threadid 是线程标识符。
void m5_work_end(uint64_t workid, uint64_t threadid);作用:标记工作线程的结束,workid 是工作标识符,threadid 是线程标识符。
- 编译程序
上面写好的c程序,就可以来编译了,编译命令如下,注意替换相应的路径:
gcc -o xxx1 xxx2 -I xxx3 -L xxx4 -lm5
- xxx1指的是编译好的文件名称,通常和程序的文件名保持一致,比如我的程序时test_m5ops.c,那么xxx1就是test_m5ops
- xxx2指的是要编译的文件名,如果当前目录下有test_m5ops.c文件,那xxx2直接就是test_m5ops.c;如果不在test_m5ops.c文件所在目录,那么xxx2就是test_m5ops.c文件的路径,相对路径和绝对路径都可以
- xxx3告诉了编译器去哪找头文件,我们在c程序中引入了一个头文件m5ops.h,那么 -I 后面的内容就是告诉编译器去哪找m5ops.h头文件,这个路径一般是include文件夹,比如我的就是/home/Downloads/gem5/include路径
- xxx4告诉编译器去哪找库文件,我们之前运行的scons build/x86/out/m5就会把库文件编译在此文件夹中,所以 -L 后面的内容就是新生成的out文件夹的路径,比如我的是/home/Downloads/gem5/util/m5/build/x86/out
-lm5指的是让编译器去链接libm5.a文件
- 分析输出
执行第4步的命令后,应该会生成一个二进制文件,比如我就生成了test_m5ops的文件。对于一般c程序来说,此时在test_m5ops文件目录下运行./test_m5ops文件,就能运行程序。
但是如果你运行了程序,你会发现控制台这样输出:
Starting simulation
illegal instruction (core dumped)
非法指令(核心已转储)
为什么会出现这样的错误呢?官方教程https://www.gem5.org/documentation/general_docs/m5ops/说,m5ops不可以用在主机上,如果想要在主机上使用m5ops的功能,使用_addr版本。
这里解释一下“不能用在主机上”是什么意思?也就是你直接在你电脑上跑test_m5ops这个二进制文件,就会报上面的错,但是你在gem5上跑,就不会有此错误。
7. 运行gem5仿真
在gem5上进行模拟了,此时回到gem5目录下,运行下面命令:
build/X86/gem5.opt /path/gem5/configs/learning_gem5/part1/two_level.py --binary /path/gem5/configs/learning_gem5/homework/test_m5ops
注意替换two_level.py和test_m5ops两个文件的路径。
- 分析输出
执行上述命令后,控制台就打印出如下信息:
Global frequency set at 1000000000000 ticks per second
build/X86/mem/mem_interface.cc:791: warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb: listening for remote gdb on port 7000
Beginning simulation!
build/X86/sim/simulate.cc:194: info: Entering event queue @ 0. Starting simulation...
build/X86/sim/mem_state.cc:443: info: Increasing stack size by one page.
build/X86/sim/syscall_emul.cc:74: warn: ignoring syscall mprotect(...)
build/X86/sim/syscall_emul.cc:74: warn: ignoring syscall mprotect(...)
build/X86/sim/syscall_emul.cc:74: warn: ignoring syscall mprotect(...)
Starting simulation
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9
Exiting @ tick 911630000 because m5_exit instruction encountered
此时看m5out目录下生成的统计信息,就是针对c程序的for循环的统计信息。
写在最后
m5ops这个功能我真的弄了很久,在网上搜了很多教程,但最后发现gem5官方教程最详细。大家也可以多去看看官方教程吼~