跟踪设施与iringbuf的建立

项目工程

Posted by Bruce Lee on 2024-08-18

关于我

欢迎来到我的博客!这里汇集了我对编程和技术的洞见和总结。本站内容分为几个主要类别,涵盖从具体技术实现到编程理念的广泛话题。

主要内容分类

  • 项目工程:深入探讨技术的实现细节和理解。
  • C/C++:围绕C/C++语言的技术点和编程技巧进行详细总结。
  • 程序员哲学:分享程序员在职业生涯中应该具备的哲学理念和思考方式。

想要了解更多具体内容,您可以访问文章分类页面。

联系我

如果您有任何问题或想要交流,欢迎通过关于页面与我联系。

感谢您的阅读和支持,希望我的博客能为您的技术旅程带来帮助!


从单步执行指令的实现原理到iringbuf实现细节

在cpu_exec函数和execute函数中有那么几个变量不是很起眼:g_nr_guest_inst和g_print_step,这两个都是全局变量.

g_nr_guest_inst很简单,就是记录执行的客户程序的指令数量.

关于单步执行的关键变量就是这个g_print_step变量.

cmd_c指令就是单步执行指令的传参n为无穷大,执行无穷多的指令.

所以如果如果si指令的参数n,如果小于10,那么就是符合程序员的想法,要单步执行10(及以下),如果大于10条,我们认为,就不是要单步执行了,而是直接执行完毕所有的指令.(10可以改变,更改MAX_INST_TO_PRINT).

如果n < MAX_INST_TO_PRINT,那么g_print_step就是true.

在execute函数中,进行for循环

这里的for循环的迭代次数就是传入的上述参数.

先是调用exec_once函数,这个函数用于进一步调用执行cpu的函数,以及进行条件定义CONFIG_ITRACE,进行一系列的信息存储.

进一步了解nemu的双线记录以及panic的输出

第一线记录就是记录到nemu-log.txt文件中:

init_log中log_file的指向,被赋值给log_fp

然后在utils.h文件还中定义了log_write宏对这一系列的写入文件和便利哪个log_fp进行包装.

对log_write进行进一步的包装就是_Log宏.

对于log_write函数以及另一条记录线在trace_and_difftest函数中运用.

这里,调用trace_and_difftest函数

trace_and_difftest函数

在第一个条件执行命令中,ITRACE_COND(默认是被定义的)

那么调用log_write宏(trace_and_difftest宏是在execute的for循环中每一条指令被执行之后就被调用.

这里就是默认就是把每一条指令执行的信息记录到nemu-log.txt文件中.

那么接下来就是另一个if语句,根据g_print_step bool变量判断是否在执行si指令,然后按照指令的规则,进行打印指令信息.

而_Log宏,进行了更加规范化的格式打印,关于FILE,LINE,func以及一些宏的变参系统.

Log宏又是对_Log宏进行的封装.

其他的信息的记录,都是通过Log宏进行的,一般在statistic函数,cpu_exec函数的后续执行中.

具体指令的执行信息的来源.

指令信息的来源,是一定要深入的执行指令的函数中.

execute不够深,而是在exec_once函数中

先明确一下,对于Decode结构体分析(之前分析过),那么里面是有一个128字节长的logbuf.

这个logbuf变量也是根据条件IFDEF中的CONFIG_ITRACE宏来决定是否会被定义.

根据CONFIG_ITRACE进行条件编译代码.

在exec_once中,logbuf变量被赋值给了p.

由p来进行处理指令地址(s->pc)

然后指令二进制码(isa.inst.val)

获取指令定义中的指令符号

然后进行反汇编.

这些全部被记录到了p变量中的地址(logbuf)

panic宏

panic宏是对Assert宏的包装,这个panic的使用是在一些未实现的函数,和内存地址溢出的时候调用.

关于Assert函数,是直接调用assert函数,程序直接退出,不会是nemu的正常清理程序.

而是直接输出尽可能多且有用的信息,然后调用assert,并不走会cpu_exec函数的那一套.

额外的错误出现信息的逻辑路径:

execute函数的for循环中,每一次循环都会检查nemu的状态.

也就是说在exec_once函数中存在设置nemu状态的代码

exec_once call isa_exec_once, isa_exec_once调用decode_exec

在decode_exec中如果匹配到了ebreak指令,会调用NEMUTRAP宏.

NEMUTRAP宏会调用set_nemu_state函数,传入NEMU_END参数,这样表示程序是完全执行完毕了.

如果decode_exec中匹配到了inv指令,那么就说明这是一条未知指令,会调用invalid_inst函数,直接传入thispc参数.

invalid_inst函数会打印出当前pc,然后告诉你,这条pc上的指令,你没有实现/指令的操作码写的不对.然后直接调用set_nemu_state函数,传入NEMU_ABORT参数.

不熟悉的综合程序错误情况

指令二进制码实现正确,但是操作出错,那么会在测试程序的check函数中出现错误,check函数会调用…

指令二进制吗实现错误,那么直接调用invalid_inst,告诉你好玩的消息,然后退出你的程序.

程序出错,出现了内存访问溢出,那么会直接输出相关消息,然后调用assert函数.

大致的信息输出和输出通道以及状态变换的几条不熟悉的路径

iringbuf实现原理

首先,在程序明显已经指示错误并且已经要退出的地方,根据上述的分析,我们可以知道不同的错误出现,都会及时的回到cpu_exec函数的switch代码块中或者回到out_of_bound函数代码中,在这两个地方进行输出buf非常合理

指令信息的来源:指令信息是采用循环结构,iringbuf,填写这个buf的时候是在trace_and_difftest函数中添加:因为根据上述的指令信息采集和处理的代码分析,我们理应该多复用已经存在的代码,并且添加更少的代码来达到合理且高效的目的.同时,这个iringbuf功能应该是和CONFIG_ITRACE_COND宏绑定在一起,这个可以在编译的时候,选择性添加的功能.

check函数的检查错误

check函数的检查,对于调试几乎没有帮助

因为check函数定义在trap.h文件中,这里调用的check函数在交叉编译的时候,会被编译成对应的跳转地址代码块.

当出现错误的时候,会跳转到check代码块,然后结束程序,并且出现bad trap.itrace会给出最后10条指令.

这里几乎不会出现有价值的调试信息.

check函数唯一给的信息,就是不对,是指令的操作码对的,但是操作不对.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !