1 前言
如下演示的是linux
内核崩溃的一个栈回溯打印,有了这个崩溃打印我们能很快定位到在内核哪个函数崩溃,大概在函数什么位置,大大简化了问题排查过程。
网上或多或少都能找到栈回溯的一些文章,但是讲的都并不完整,没有将内核栈回溯的功能用于实际的内核、应用程序调试,这是本篇文章的核心:尽可能引导读者将栈回溯的功能用于实际项目调试,栈回溯的功能很强大。
本文详细讲解了基于mips
、arm
架构linux
内核栈回溯原理,通过不少例子,尽可能全面给读者展示各种栈回溯的原理,期望读者理解透彻栈回溯。在这个基础上,讲解笔者近几年项目开发过程中使用linux
内核栈回溯功能的几处重点应用。
1当内核某处陷入死循环,有时运行sysrq
的内核线程栈回溯功能可以排查,但并不适用所用情况,笔者实际项目遇到过。最后是在系统定时钟中断函数,对死循环线程栈回溯20
多级终于找到死循环的函数。
2当应用程序段错误,内核捕捉到崩溃,对崩溃的应用空间进程/线程栈回溯,像内核栈回溯一样,打印应用段错误进程/线程的层层函数调用关系。虽然运用core
文件分析或者gdb
也很简便排查应用崩溃问题,但是对于不容易复现、测试部偶先的、客户现场偶先的,这二者就很难发挥作用。
还有就是如果崩溃发生在C
库中,CPU
的pc
和lr
(arm
架构)寄存器指向的函数指令在C
库的用户空间,很难找到应用的代码哪里调用了C
库的函数。
arm
架构网上能找到应用层栈回溯的例子,但是编译较麻烦,代码并不容易理解,况且mips
能在应用层实现吗?还是在内核实现应用程序栈回溯比较方便。
3 应用程序发生double free
,运用内核的栈回溯功能,找到应用代码哪里发生了double free
。double free
是C
库层发现并截获该事件,然后向当前进程/线程发送SIGABRT
进程终止信号,后续就是内核强制清理该进程/线程。double free
比应用程序段错误更麻烦,后者内核还会打印出错进程/线程名字、pid
、pc
和lr
寄存器值,double free
这些打印全没有。
笔者做过的一个项目,发布前,遇到一例double free
崩溃问题,极难复现,当初要是把double free
内核对出问题进程/线程栈回溯的功能做进内核,就能找到出问题的应用函数了。
4 当应用程序出现锁死问题,对应用所有线程栈回溯,分析每个线程的函数执行流程,对查找锁死问题有帮助。
以上几例应用,在笔者所做的项目中,内核已经合入相关代码,功能得到验证。
2 栈回溯的原理解释
2.1 基于fp栈帧寄存器形式的栈回溯
笔者最初学习栈回溯,首先看到的资料就是arm
架构基于fp
寄存器的栈回溯,这种资料网上比较多,这里按照自己理解再描述一遍。
这种形式的栈回溯相对来说并不复杂,也容易理解,遵循APCS(ARM Procedure Call Standard)
规范, APCS
规范了arm
寄存器的使用、函数调用过程出栈和入栈的约定。<