问题描述
本文通过gdb调试的方法来弄懂nemu大概执行流程,由此找到报错的源头并加以解决,其实很多内容是不必要的,但为了能够更清晰地理解nemu框架代码,导致本文看起来比较繁琐
调试全过程
进入tmux,方便后续调试
首先,我们可以通过find找到nemu的main函数在哪里(在nemu目录下)
find ./ -name '*' | xargs grep 'main' 2>/dev/null
会发现输出了很多行的结果,往下找,找到可能性最大的那个:
但这是源代码,用gdb调试的话,需要找到编译后的文件,这些文件全放在/nemu/build目录下,其中obj-riscv32-nemu-interpreter /src下面放每个源代码编译后的结果,而riscv32-nemu-interpreter才是我们需要关注的文件,输入gdb进入调试,file到riscv32-nemu-interpreter里面
用list查看源码,在main函数处设置断点、随后运行
然后输入n,跳过init_monitor函数,进入engine_start,再进入sdb_mainloop
经过rl_gets的时候,输入q,通过后面的调试看看为什么会报错
当走到下面这个位置的时候就会进入cmd_q
由于直接返回-1,导致return,跳出sdb_mainloop
回到main函数,继续执行,进入is_exit_status_bad函数,打印nemu_state.state值,发现是1
但暂时还不知道NEMU_END这些常量,以及nemu_state.state的初值在哪里定义,在右边开一个窗口,搜这些量,可以发现在src/utils/state.c中初始化了.state为NEMU_STOP,同时在下面的util.h中,看到了NEMU_STOP这个值为1也正是左边的gbd输出值
good的值是:
由于nemu_state.state是NEMU_STOP,导致good==0,return为1所以报错
于是有一个猜想:
先输入c再输入q不报错是因为改变了nemu_state.state的值使得good == 1
为了证实这个猜想,gdb重新运行代码,这次,在该输入指令的地方首先输入c指令、再输入q
这次nemu_state.state的值变成了2,也就是对应的NEMU_END,到底在哪里发生改变了呢?
回到sdb_mainloop,当指令为c时,执行cmd_c,进入cpu_exec
继续往下进行会发现cpu_exec会执行execute,然后又执行exec_once然后又执行其它,由于代码比较多而且很多细节也看不懂,可以先用find去寻找一下"NEMU_END"的字样,发现有一个:set_nemu_state(NEMU_END, thispc, code),很可能就是让状态值改变的原因,于是继续搜索这个函数所在的文件、查看函数定义:
终于找到了让state改变的原因!
最终结论
所以现在的猜想就是:
输入c——》调用cpu_exec——》调用execute——》调用exec_once——》。。。。。。——》调用set_nemu_state——》修改nemu_state.state——》good=1——》再输入q——》不报错
直接输入q——》good=0——》报错
至于。。。。。。哪一部分调用了那个函数可以通过gdb一直往后next找到,此处就不作演示了
好了,回到主题上,现在想让最后return 0,只需让good=1
所以:只需在调用cmd_q的时候,把state改为:NEMU_QUIT即可
如下,不再报错