https://lwn.net/Articles/728339/

@据德

在kernel中获取正确的调用栈是一项重要的功能,例如profiling,tracing,调试内核crash等。然而从历史来看,kernel的堆栈跟踪并不靠谱,原因有很多,虽然大部分在近几年已经修复。现在看来,4.14版本的内核可能会包含一种新机制,能解决目前堆栈跟踪存在的若干问题。

kernel的调用栈解析出人意料的难。正常来讲,调用栈主要由c函数调用组成,但是kernel中的汇编语言、中断、陷阱门等增加了解析复杂度。令人费解的堆栈,可能使得unwinder产生一些奇怪的行为,所以内核代码里尽量避免复杂的unwind逻辑。因此,内核开发者在处理堆栈跟踪问题时,不得不学会处理偶发性的一些坏数据(即无法获取正确的调用栈)。

live patching依赖于完全可靠的堆栈信息来保证一致性,简而言之,它需要能够识别出一个特定的函数是否正处于系统中所有线程的调用栈里。为了实现这个目标,需要分两步走。首先,编译时栈有效机制确保kernel代码维护了正确的堆栈元数据信息;其次,则是需要一个unwinder能够基于这些元数据信息来进行正确的堆栈解析。

已有的一类unwinder实现:基于编译器生成的DWARF调试信息。但在17年5月份,Linus Torvalds提到这类unwinder在汇编语言或编译器生成不正确的DWARF调试信息时,将无法正常运行,不可维护。Torvalds明ç¡®提到“我再也不想在oppsing代码中看到使用了复杂状态机的unwinder”。所以,DWARF的复杂性,意味着这并不是unwinder实现的一个好选择。

这个现状也许就此终结,Josh Poimboeuf提到了一个思路:objtool工具已经支持在编译期做栈的有效性检查,也许可以更进一步地,通过objtool来生成比DWARF更加简洁的调试信息。这样,将有希望实现一种基于高效数据结构且可靠的unwinder。更重要的是,这将完全处于内核社区的控制下(相比于DWARF受限于编译器版本),将更加稳定。

两个月后,ORC(oops rewind capability) unwinder出现了,其核心数据结构非常简洁:

struct orc_entry {
    	s16		sp_offset;
		s16		bp_offset;
			unsigned	sp_reg:4;
				unsigned	bp_reg:4;
					unsigned	type:2;
					    };

unwinder通过orc_entry来定位堆栈,内核的每一个可执行地址均关联了其中一个结构。通过简单的数据结构,unwinder可以找到程序计数器地址的相应entry。该数据结构的解释依赖于type字段。如果是ORC_TYPE_CALL,则表示是c风格的调用栈帧,帧的起始位置可通过sp_offset加上sp_reg表示的寄存器中的值(即sp_offset + REG(sp_reg))得到。如果是ORC_TYPE_REGS,则计算的和指向pt_regs数据结构,其描述的是执行系统调用前的状态(即用户态)。如果是ORC_TYPE_REGS_IRET ,则通过sp_reg以及sp_offset可以找到硬件中断的返回栈帧。目前这三种状态,基本上可以涵盖所有的场景,至少在x86上是如此。(bp_reg以及bp_offset在目前的实现中并未使用到)。

ORC新机制比DWARF机制更加简洁,性能更好(号称至少快了20倍)。性能的提升或许对kernel oops并没有什么用,但对tracing以及profiling则很有用。相比于frame pointer机制,OCR号称更加可靠;关键是ORC性能更好(因为frame pointer需要执行额外的指令来保存bp值)。(译者注:关于frame pointer,DWARF,ORC更详细的对比可参考The Linux x86 ORC Stack Unwinder)。如上文提到,ORC格式完全在kernel社区的掌控之下,一般情况下不受编译器更新迭代的影响;即使受到影响,kernel开发者也应该可以迅速修复。

当然,很难预测编译器开发者是否会打破目前的调用栈信息,使得ORC无法正常工作。 Poimboeuf承认补丁存在上述风险,也提到:“如果新版GCC的优化使得objtool无法正常工作,我们则应该回过头来review当前的实现。另外的思路则是要求GCC的优化需要具有可移植性,或者objtool也支持DWARF格式,或者添加GCC插件来辅助objtool进行分析。”ORC的另一缺点则是比DWARF格式占用了更多的内存空间,大约1M左右。Poimboeuf表示,如果事实证明这是一个真正的问题,可通过一些手段来降低内存使用。不过,这将牺牲速度以及简洁性。

Torvalds还没有发表关于ORC补丁的看法,但他曾提到过:使用objtool与一种简单的数据格式来实现unwinder(与ORC不谋而合)。Ingo Molnar已将补丁打应用到tip tree,所以很可能在4.14版本提交merge request。除上文提到的最后几个问题,通过这几年的努力,一种可靠的堆栈unwinder将接近完成。