OpenXiangShan / GEM5

BSD 3-Clause "New" or "Revised" License
63 stars 22 forks source link

The segment fault occurred during the recovery process from a checkpoint generated by NEMU #166

Closed zybzzz closed 2 months ago

zybzzz commented 2 months ago

在使用 XS-GEM5 恢复 NEMU 产生的检查点时产生段错误。


环境描述:

  1. 操作系统:Linux Mint 21.3(ubuntu 22.04 jammy)
  2. gem5: xs-dev de13e7e4f7ce65291b64faf99494d044aa3790ba
  3. NEMU: master 732e4ccdda9f3f5111cd5701a6eca8d887dfb025
  4. 交叉编译工具链:riscv-gnu-toolchain的预编译版本 其版本为 13.2.0 其他环境同b站checkpoint生成示范视频

    问题发现过程: 我想使用xs-gem5进行自己的体系结构探索。我按照b站的视频编译了 SPEC2006 的 benchmark,编译选项全部为仓库默认,在编译完 benchmark 之后我使用 qemu-riscv跑了我想用的 benchmark,并没有报错。随后我按照视频过程将 benchmark打包到了riscv-pk 中,并使用 nemu 仓库中给定的默认脚本进行 checkpoint 的生成。随后成功生成了多个 checkpoint,我使用 nemu 恢复 checkpoint,均正常退出。随后希望将这些 checkpoint 从 gem5 中恢复从而进行体系结构探索。

我首先将 gem5 切换到上文给定的分支,并随机选取了一个benchmark,按照 README 中的要求执行默认脚本,gem5正确退出并生成统计数据结果(由此,我以为整个环境就搭建完成了,实际上应该是一种错觉)。随后我自己对xs-gem5中的代码进行了相关的改动,我期望将 checkpoint 运行于我改动过后的 xs-gem5 之上,来查看自己改动xs-gem5相关结构的结果,在解决完多个我自己程序的问题之后,我发现xs-gem5 仍然会在运行 checkpoint 的时候报出段错误,我在运行命令的脚本中增加了 debug-flag的选项:

$gem5 --debug-flag=IEW,Commit,LSQUnit \
  --debug-file=debug.log \
  $gem5_home/configs/example/xiangshan.py --generic-rv-cpt=$1

并运行命令:

bash ../kmh_6wide.sh spectest/bbl/2747/_2747_0.070885_.gz

在 debug.log 的最后几行显示:

4205350440: system.cpu.iq: before exit method if
4205350440: system.cpu.iew: Execute: Executing instructions from IQ.
4205350440: system.cpu.iew: Execute: Processing PC (0xd4898=>0xd489a).(0=>1), [tid:0] [sn:44882849].
4205350440: system.cpu.iew: Execute: Calculating address for memory reference.
4205350440: system.cpu.iew.lsq.thread0: Executing load PC (0xd4898=>0xd489a).(0=>1), [sn:44882849]
4205350440: system.cpu.iew.lsq.thread0: before init acc
4205350440: global: RegFile: Access to int register 138, has data 0xa80fd0
4205350440: system.cpu.iew.lsq.thread0: request: size: 8, Addr: 0xa81000
4205350440: system.cpu.iew.lsq.thread0: Read called, load idx: 1094785, store idx: 255910, storeHead: 255909 addr: 0xf894d000
4205350440: system.cpu.iew.lsq.thread0: Doing memory access for inst [sn:44882849] PC (0xd4898=>0xd489a).(0=>1)

对于日志的分析显示好像是在对gem5中内存进行访问的时候报出了段错误。起先我认为是自己的改动问题,但是我自己的代码本身没有对LSQ进行修改。于是我尝试将xs-gem5切换回未改动的分支运行相同的checkpoint:

bash ../kmh_6wide.sh spectest/bbl/2747/_2747_0.070885_.gz

运行这个 checkpoint 的时候未改动的 xs-gem5 没有报错。我尝试使用别的 checkpoint:

bash ../kmh_6wide.sh spectest/bbl/92/_92_0.008645_.gz

未改动的xs-gem5仍然产生同样的段错误,日志内容也相似。 产生的日志内容最后显示:

4172118372: system.cpu.iew: [tid:0] dispatchStalls[4]=7
4172118372: system.cpu.iew: [tid:0] dispatchStalls[5]=7
4172118372: system.cpu.iew: Execute: Executing instructions from IQ.
4172118372: system.cpu.iew: Execute: Processing PC (0x17242a=>0x17242e).(0=>1), [tid:0] [sn:46511589].
4172118372: system.cpu.iew: Execute: Calculating address for memory reference.
4172118372: system.cpu.iew.lsq.thread0: Executing load PC (0x17242a=>0x17242e).(0=>1), [sn:46511589]
4172118372: global: RegFile: Access to int register 2, has data 0x9134c0
4172118372: system.cpu.iew.lsq.thread0: request: size: 2, Addr: 0x9134c0
4172118372: system.cpu.iew.lsq.thread0: Read called, load idx: 7686276, store idx: 3370520, storeHead: 3370515 addr: 0x80de74c0
4172118372: system.cpu.iew.lsq.thread0: Doing memory access for inst [sn:46511589] PC (0x17242a=>0x17242e).(0=>1)

虽然访问的地址不同,但是貌似是同样的原因产生了段错误,上述的两个过程中 difftest 均没有报错。随后我开始对 checkpoint 进行相关的检测,我挑选了几个 checkpoint ,同样放在未修改过的 xs-gem5 上跑,发现对于 nemu 能够正确恢复的 checkpoint,在 xs-gem5 上并不能够正确恢复。我使用的 checkpoint 在这个链接中能够进行下载,我将未修改的xs-gem5中能正常退出的 checkpoint 放在 right 目录下,在未修改的xs-gem5中产生段错误的检查点放在 error 目录下。至于对于我自己修改过的 xs-gem5 ,上面所有的检查点都会产生段错误。

我想请问这种情况可能是什么原因导致的?应当如何解决。以上的代码都是我上个星期从仓库获取的,考虑到香山更新速度快,我会尝试切换到更新的提交按照 README 再进行尝试。

另外我想问的是:

  1. 对于 nemu 生成的 checkpoint,xs-gem5并不一定能够正确恢复,这是一个正常现象吗?还是这本身就是一个不应该出现的情况。
  2. 对于 xs-gem5 的开发,在开发过程中难免要进行调试。请问香山团队采取的调试方法是 difftest 结合 dprintf 产生 log 的方法进行调试的吗?
  3. 对于上面这样的问题,根据日志能够确定发生错误的时机在 4172118372 ticks 之后,香山团队在调试类似问题的时候会采用 gdb 进行调试,然后在 4172118372 ticks 停下然后进行调试的方法吗,我还没有验证过这样的方法是否可行。

刚刚接触 xiangshan 系列的项目,理解欠佳,如果有理解错误的地方烦请见谅。谢谢!


其他关键信息:

  1. benchmark编译选项为SPEC2006LiteWrapper仓库的默认选项:

    CFLAGS   += $(COPTIMIZE) -MMD -DSPEC_CPU -DNDEBUG -static -D_FILE_OFFSET_BITS=64 -fno-strict-aliasing -Wno-implicit-int -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-implicit-function-declaration $(TESTSET_SPECIFIC_FLAG)

    在编译的时候并没有开启向量化的支持,因此编译出的程序只是包含 riscv64GC 的,因此我按照 README,设置 $GCB_REF_SO$GCB_RESTORER,全程都是开启 difftest 的,上面产生报错的过程 difftest 均没有出现问题。

  2. 作为运行的 benchmark,我挑选了 bbl 中的 gcc benchmark 打包进 bbl,initramfs.txt 中的 benchmark 部分为:

    
    dir /bin 755 0 0
    dir /etc 755 0 0
    dir /dev 755 0 0
    dir /lib 755 0 0
    dir /proc 755 0 0
    dir /sbin 755 0 0
    dir /sys 755 0 0
    dir /tmp 755 0 0
    dir /usr 755 0 0
    dir /mnt 755 0 0
    dir /usr/bin 755 0 0
    dir /usr/lib 755 0 0
    dir /usr/sbin 755 0 0
    dir /var 755 0 0
    dir /var/tmp 755 0 0
    dir /root 755 0 0
    dir /var/log 755 0 0

nod /dev/console 644 0 0 c 5 1 nod /dev/null 644 0 0 c 1 3

libraries

file /lib/ld-linux-riscv64-lp64d.so.1 ${RISCV}/sysroot/lib/ld-linux-riscv64-lp64d.so.1 755 0 0 file /lib/libc.so.6 ${RISCV}/sysroot/lib/libc.so.6 755 0 0 file /lib/libresolv.so.2 ${RISCV}/sysroot/lib/libresolv.so.2 755 0 0 file /lib/libm.so.6 ${RISCV}/sysroot/lib/libm.so.6 755 0 0 file /lib/libdl.so.2 ${RISCV}/sysroot/lib/libdl.so.2 755 0 0 file /lib/libpthread.so.0 ${RISCV}/sysroot/lib/libpthread.so.0 755 0 0

busybox

file /bin/busybox ${RISCV_ROOTFS_HOME}/rootfsimg/build/busybox 755 0 0 file /etc/inittab ${RISCV_ROOTFS_HOME}/rootfsimg/inittab-spec 755 0 0 slink /init /bin/busybox 755 0 0

SPEC common

dir /spec_common 755 0 0 file /spec_common/before_workload ${RISCV_ROOTFS_HOME}/rootfsimg/build/before_workload 755 0 0 file /spec_common/trap ${RISCV_ROOTFS_HOME}/rootfsimg/build/trap 755 0 0

SPEC

dir /spec 755 0 0 file /spec/run.sh ${RISCV_ROOTFS_HOME}/rootfsimg/run.sh 755 0 0 file /spec/gcc_base.gcc ${SPEC_BUILD_PATH}/gcc_base.gcc 755 0 0 file /spec/scilab.i ${CPU2006_RUN_DIR}/gcc/scilab.i 755 0 0

3. 对于 bbl 恢复的地址,我仍然将 bbl 的恢复地址保持为 0x800a0000,而不是现在官方文档上的 0x80100000,先前我曾经按照官方文档将恢复地址设置成 0x80100000,在这种情况下生成的检查点不管是 xs-gem5 中 README 提及的哪一种方案,xs-gem5都无法正确运行,因此我仍然将 bbl 恢复地址保持在默认的 0x800a0000 下。

对于 riscv-pk目录,我并没有按照官网的改动,而是进行如下的改动:
```diff
diff --git a/bbl/bbl.lds b/bbl/bbl.lds
index 9d51f1d..44ddda4 100644
--- a/bbl/bbl.lds
+++ b/bbl/bbl.lds
@@ -12,7 +12,7 @@ SECTIONS
   /*--------------------------------------------------------------------*/

   /* Begining of code and text segment */
-  . = MEM_START;
+  . = MEM_START + 0xa0000;
   _ftext = .;

   .text :

riscv-pk 下的 noop.dtsi 并无改动,最关键的是 memory 部分没有进行改动仍然保持为:

L11: memory@100000000 {
        device_type = "memory";
        reg = <0x0 0x80000000 0x0 0x80000000>;
    };

对于 nemu 下的 gcpt.lds 并没有改动,通过反汇编能够发现,gcpt.bin 这边 bbl 恢复的地址确实是 0x800a0000。

shinezyy commented 2 months ago

您的 bug 报告简直 awesome!我会尽快尝试复现这个 bug

我会尝试把你的 bug report 做成 issue 模板,非常感谢!

  1. 对于 nemu 生成的 checkpoint,xs-gem5并不一定能够正确恢复,这是一个正常现象吗?还是这本身就是一个不应该出现的情况。 在版本正确时,99%的情况下不应该出现
  2. 对于 xs-gem5 的开发,在开发过程中难免要进行调试。请问香山团队采取的调试方法是 difftest 结合 dprintf 产生 log 的方法进行调试的吗? difftest + dprintf + gdb
  3. 对于上面这样的问题,根据日志能够确定发生错误的时机在 4172118372 ticks 之后,香山团队在调试类似问题的时候会采用 gdb 进行调试,然后在 4172118372 ticks 停下然后进行调试的方法吗,我还没有验证过这样的方法是否可行。

这是我用 gdb 调试的时候修改脚本的方法: image

我一般只看看 backtrace,或者 watch 一个 host address。gem5 在 gdb 的符号解析不一定好用

zybzzz commented 2 months ago

感谢您的帮助,目前我自己这边会在后面几天通过更新工具的版本来尝试解决这个问题,但是提出issue版本下的bug复现以及需要您那边的帮助。 补充更多的版本信息:

  1. spec2006litewrapper: 06a53ed2e2e8cfd9acf1516777e4d521db1951cb
  2. riscv-rootfs: checkpoint分支 7b6cf0ccdfdb103d2e5918712036634d4e973552
  3. riscv-linux: nanshan分支 655055af981b490cb6a12353a5bb846d2be79c6f
  4. riscv-pk: noop分支 a621e1620a1ab050d81f53743253e8eb5cf1b24e
  5. NEMU:reference design 和 restorer 均为 master 732e4ccdda9f3f5111cd5701a6eca8d887dfb025
  6. xs-gem5: xs-dev de13e7e4f7ce65291b64faf99494d044aa3790ba

谢谢!

您的 bug 报告简直 awesome!我会尽快尝试复现这个 bug

我会尝试把你的 bug report 做成 issue 模板,非常感谢!

  1. 对于 nemu 生成的 checkpoint,xs-gem5并不一定能够正确恢复,这是一个正常现象吗?还是这本身就是一个不应该出现的情况。 在版本正确时,99%的情况下不应该出现
  2. 对于 xs-gem5 的开发,在开发过程中难免要进行调试。请问香山团队采取的调试方法是 difftest 结合 dprintf 产生 log 的方法进行调试的吗? difftest + dprintf + gdb
  3. 对于上面这样的问题,根据日志能够确定发生错误的时机在 4172118372 ticks 之后,香山团队在调试类似问题的时候会采用 gdb 进行调试,然后在 4172118372 ticks 停下然后进行调试的方法吗,我还没有验证过这样的方法是否可行。

这是我用 gdb 调试的时候修改脚本的方法: image

我一般只看看 backtrace,或者 watch 一个 host address。gem5 在 gdb 的符号解析不一定好用

shinezyy commented 2 months ago

The bug is fixed in #168

zybzzz commented 2 months ago

感谢您的帮助,我会在获取最新的提交之后关闭这个issue。谢谢!

shinezyy commented 2 months ago

Then let me show how to debug it:

  1. First modify the running script to use gdb: image

  2. Then after it crashes, here is the backtrace: image

  3. There are several key info in backtrace:

    • A memory request hit in L1 and notifies CDP prefetch in L2
    • Becaues CDP is data-dependent, it tried to obtain cache block from L2
    • Segmentation fault is encountered in findBlock(

image

Before this patch, findBlock seems to assume blk is always non-null. Maybe it was always called when there is a cache hit.

However

This might be the reason of the bug

zybzzz commented 2 months ago

我在 xs-gem5 和我修改过的 xs-gem5 均进行了 #168 的测试,对于原先报错的 checkpoint 均正常退出,这个bug应当已经被正确修复。 对于调试我下次也会尝试用 gdb bt 的方法,对于这种段错误的 panic 来说,bt 显得非常有效。碍于我的程序调试经验,我先前并没有想到这种办法,下次会进行尝试,感谢指点以及对 bug 的快速修复。


This bug has been fixed in #168, thanks to the xiangshan team for the quick fix support!