QEMU可以直接引导内核和initramfs:
qemu-system-x86_64 -kernel /boot/vmlinuz-4.13.9-gentoo -initrd initramfs.gz -append 'console=ttyS0' -smp 8 -nographic -serial mon:stdio -cpu host -enable-kvm
并没有任何信息输出到控制台上…… 我意识到我没有为标记本电脑内核编译8250串行端口支撑。哦,我太蠢了。它根本没有物理串口,对吧?不管怎么样,我从新编译了内核,并附带串行支撑。我再试了一下,它成功启动并运行了。
它崩溃了吗?是的。太好了,这意味着这个问题在同一台机械上的虚拟机上是可以重现的。我在家里的办事器上用同样的QEMU敕令,用本身的内核,但什么也没有产生。然后,我大年夜标记本电脑中把内核复制过来,然后启动,崩溃了。内核是问题的关键,硬件不是问题。
捣鼓内核
我意识到本身须要编译很多的内菏攀来测验测验才能缩小范围,所以,我决定转移到一台最强大年夜的机械上来:一个有点旧的12核24线程Xeon处理器的机械。我将已知的坏内核源复制到那台机械上,构建并进行测试。
它竟然没有崩溃!为什么?
在细心思考了一番之后,我已经可以或许肯定是本来的坏的内核二进制文件崩溃了。我们要回到分析硬件的问题上去吗?跟我在哪台机械上编译内核有关吗?所以,我试着在家用办事器上编译内核,接着,这个崩溃急速触发了。在两台机械上构建雷同的内核会导致崩溃,而第三台机械不会。它们之间有什么不合呢?
然则我的家用办事器内核与标记本电脑内核几乎是雷同的(尽管不完全雷同),应用雷同的GCC构建,并没有重现崩溃。所以,如今我们必须得出结论:用来构建内核的编译器和内核本身(或其设备?)都有问题。
为了进一步缩小范围,我在家用办事器(linux-4.13.9-gentoo)上编译了标记本电脑上的内核树,并确认它出现了崩溃。然后,我把家用办事器上的.config复制过来并编译,发明它没有崩溃。这么做是因为我们想要寻找内核设备之间的差别和编译器之间的差别:
- linux-4.13.9-gentoo + gcc 5.4.0-r3 p1.3 + laptop .config - 没有崩溃
- linux-4.13.9-gentoo + gcc 6.4.0 p1.0 + laptop .config - 崩溃
- linux-4.13.9-gentoo + gcc 6.4.0 p1.0 + server .config - 没有崩溃
两个.config,一个好,一个坏。须要一点时光来查看它们之间的差别。当然,这两个设备文件是完全不合的(因为我爱好定制我的内核设备,让它只包含特定机械上所需的驱动法度榜样),所以我不得不在反复编译内菏攀来缩小差别。
我决定大年夜“坏”的.config开端着手,大年夜中删除一些器械。因为要测试崩溃须要等待必定的时光,所以测试“崩溃”比“不崩溃”更轻易。在22个内核的构建过程中,我对 config 文件做了简化,使其不支撑收集、没有文件体系、没有块设备核心,甚至不支撑PCI(但它仍然可以在虚拟机上正常工作!)。如今编译一下内核不到60秒的时光,内核大年夜小大年夜约是我常用内核的四分之一阁下。
然后,我转移到“好”的.config文件上来,删除了所有不须要的垃圾,同时确保它不会崩溃(这比之前的测试加倍棘手加倍慢)。也有一些有问题的分支,我在这些分支上修改了一些器械,接着就开端崩溃了。然则,我误认为这些分支是“不会崩溃”的。所以,当崩溃产生的时刻,我不得不找回以前的内核,并找出引起崩溃切实其实切的原因。最后,我一共编译了7个内核。
毫无疑问,对于上游问题,起首能想到的是这是一个硬件问题。毕竟我只是在一台特定的机械上碰着这个问题。其他所有的机械都能很顺利地运行node_exporter。固然在这台主机膳绫腔有其他硬件连接不稳定的证据,然则我也无法解释这台机械存在能导致node_exporter崩溃的特别性。Memtest86+的运行永远不会破坏其他法度榜样,所以我安装了一个。
最后,我把范围缩小到.config中的几个不合的选项上来。个中有几个嫌疑很大年夜,特别是CONFIG_OPTIMIZE_INLINING。经由细心地测试,我得出结论:这个选项就是祸首祸首。把它关掉落,就会产生崩溃,启用,就不会崩溃。这个选项在打开的时刻许可GCC本身肯定哪个inline函数真的须要内联,而不是强迫内联。这也说清楚明了:内联行动可能跟着GCC版本的不合而不合。
/* * Force always-inline if the user requests it so via the .config, * or if gcc is too old. * GCC does not warn about unused static inline functions for * -Wunused-function. This turns out to avoid the need for complex #ifdef * directives. Suppress the warning in clang as well by using "unused" * function attribute, which is redundant but not harmful for gcc. */#if !defined(CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING) || \ !defined(CONFIG_OPTIMIZE_INLINING) || (__GNUC__ < 4)#define inline inline __attribute__((always_inline,unused)) notrace#define __inline__ __inline__ __attribute__((always_inline,unused)) notrace#define __inline __inline __attribute__((always_inline,unused)) notrace#else/* A lot of inline functions can cause havoc with function tracing */#define inline inline __attribute__((unused)) notrace#define __inline__ __inline__ __attribute__((unused)) notrace#define __inline __inline __attribute__((unused)) notrace#endif
那么接下来做什么呢? 我们知道CONFIG_OPTIMIZE_INLINING这个选项使得测试结不雅出现不合,然则它可能会改变┞符个内核中每一个inline的行动。 若何查明问题的┞锋相呢?
我有一个主意。
基于散列的差别化编译
我们要做的是在选项打开的情况下编译内核的一部分,在选项封闭的情况下编译另一部分。 经由过程测试生成的内核并检盘考题是否重现,可以推导出内核编译单位的哪个子集的代码有问题。
我没有列举出所有的目标文件,或是进行某种二分法搜刮,而是决定采取基于散列的办法。 我为GCC编写了这个包装器脚本:
推荐阅读
NVIDIA Titan V显卡拆解:211亿晶体管堆出巨型怪物
NVIDIA近日忽然宣布了第一款基于下代12nm Volta架构核心的显卡Titan V(此前的Tesla V100是计算卡),轻轻松松成为地球上最强大年夜的显卡。Titan V基于最高规格的GV100核心,集成211亿个晶>>>详细阅读
本文标题:Go运行时,对bug的分析调试过程解析
地址:http://www.17bianji.com/lsqh/39892.html
1/2 1