愚蠢的RCU技巧:争取调试器的帮助

2020-09-08 03:46:47

所以rcutort发现了一个bug,您已经知道了如何重现它,git二等分没有什么帮助(可能是因为这个bug一直存在),而这个bug恰好是那些罕见的RCU bug之一,调试器可能会对它有所帮助。你能做什么?传统上,我所做的是中途弄清楚如何让gdb与rcutorat一起工作,然后突然意识到bug的根本原因肯定是什么。在这一点上,我当然放弃了gdb,转而支持修复这个bug。因此,尽管我在过去20年中多次尝试将gdb应用于Linux内核,但实际上从未成功过。现在,这并不是说gdb对Linux内核黑客毫无用处。差得远呢!。首先,尝试使用gdb的行为激发了我认识到许多bug的根本原因,这意味着它已经起到了很好的生产力辅助作用。另一方面,我经常将Linux内核代码提取到用户模式脚手架中,并在该上下文中使用gdb。最后,确实有很多Linux内核黑客经常使用GDB。奥马尔·桑多瓦尔(Omar Sandoval)就是其中一名黑客,他碰巧提到过他曾使用gdb追踪一个Linux内核错误。并且无需首先将代码提取到用户空间。我想是时候让这只老狗学个新把戏了,所以我问奥马尔他是怎么做到的。Omar指出,因为rcutorat运行在来宾操作系统中,所以gdb可以利用QEMU提供的调试支持。要做到这一点,您需要使用CONFIG_DEBUG_INFO=y构建内核(它为gdb提供额外的符号),提供nokaslr内核引导参数(这可以防止内核地址空间随机化使这些符号无效),并为qemu提供-s-S命令行参数(这会导致它等待gdb连接,而不是立即引导内核)。然后将vmlinux文件的路径名指定为gdb的唯一命令行参数。一旦看到(Gdb)提示符,target remote:1234命令将连接到QEMU,然后Continue命令将引导内核。我试过了,效果很不错。或者,您现在可以在-rcu树中使用漂亮的新rcucutch--gdb命令行参数,它将自动设置内核和QEMU,并打印出所需的gdb命令,包括新构建的vmlinux文件的路径。是的,我确实欠Omar一个--drgn命令行参数,一旦他告诉我如何将drgn连接到QEMU,我就会提供这个参数。:-)同时,以下各节介绍了我使用--gdb的几个用法,主要是为了练习这种Linux内核调试方法。例如,让使用gdb在运行Scenario LOCK05时使用gdb调查长期存在的锁定酷刑挂起:一旦构建内核并启动qemu,这将输出以下内容:等待您附加调试会话,例如:gdb gdb/home/git/linux-rcu/tools/testing/selftests/rcutorture/res/2020.08.27-14.51.45/LOCK05/vmlinuxAfter Symbols Load,并出现提示:Target Remote:1234 Continue。

一旦启动了gdb并输入了建议的两个命令,内核就会启动。您可以通过查找其console.log文件来跟踪其控制台输出,如前面的帖子中所述。或者,您可以使用ps命令转储QEMU命令行,查找-Serial file:命令,该命令后跟接收控制台输出的文件的路径名。一旦内核充分挂起,即在最后一个统计数据输出行之后超过15秒(写入:总计:27668769最大/最小:27403330/34661失败:0),您可以在gdb按Ctrl-C组合键。通常的INFO THREADS命令将显示CPU的状态,这里的64位十六进制地址缩写为:(GDB)INFO THREADS ID Target ID FRAME*1Thread 1(CPU#0[Running])STUTTER_WAIT(title=0xf...。";lock_tort_Writer";)在kernel/torture.c:615 2Thread 2(CPU#1[运行])0xf...。在STUTTER_WAIT(标题=0xf...。位于kernel/torture.c:615 3线程3(CPU#2[HALTED])DEFAULT_IDLE()位于ARCH/x86/kernel/process.c:689 4线程4(CPU#3[HALTED])DEFAULT_IDLE()位于ARCH/x86/kernel/process.c:689。

奇怪的是,CPU#0和#1都在STUTTER_WAIT()中,在全局变量STUTTER_PAUSE_TEST上旋转。更奇怪的是,这个变量的值不是零(测试结束时应该是零),而是值二。毕竟,CONFORKUT_STUTTER()的所有路径都应该将该变量置零。但是,在将STUTTER_PAUSE_TEST清零之前,torat_stutter()可能仍然停留在循环中。快速查看torat_stutter_init会发现,指向运行torat_stutter的任务的task_struct指针驻留在stutter_task中,它是非空的,这意味着该任务仍然有效。您可能希望使用sched_show_task(),但遗憾的是这失败了,并显示无法获取寄存器