Linux Call Trace

内核态call trace

内核态有三种出错状况,分别是bug, oopspanic 函数

bug属于轻微错误,好比在spin_lock期间调用了sleep,致使潜在的死锁问题,等等。 oop

oops表明某一用户进程出现错误,须要杀死用户进程。这时若是用户进程占用了某些信号锁,因此这些信号锁将永远不会获得释放,这会致使系统潜在的不稳定性。 ui

panic是严重错误,表明整个系统崩溃。 spa

 

OOPS 指针

先介绍下oops状况的处理。Linux oops时,会进入traps.c中的die函数。 调试

int die(const char *str, struct pt_regs *regs, long err) orm

       。。。 进程

       show_regs(regs); rpc

 

void show_regs(struct pt_regs * regs)函数中,会调用show_stack函数,这个函数会打印系统的内核态堆栈。 回调函数

具体原理为:

       从寄存器里找到当前栈,在栈指针里会有上一级调用函数的栈指针,根据这个指针回溯到上一级的栈,依次类推。

       powerpcEABI标准中,当前栈的栈底(注意是栈底,不是栈顶,即Frame Header的地址)指针保存在寄存器GPR1中。在GPR1指向的栈空间,第一个DWORD为上一级调用函数的Frame Header指针(Back Chain Word),第二个DWORD是当前函数在上一级函数中的返回地址(LR Save Word)。经过此种方式一级级向上回溯,完成整个call dump。除了这种方法,内建函数__builtin_frame_address函数理论上也应该能用,虽然在内核中没有见到。(2.6.29的ftrace模块用到了__builtin_return_address函数)。

 

show_regs函数在call trace的时候,只是用printk打印了一下栈中的信息。若是当前系统没有终端,那就须要修改内核,把这些栈信息根据需求保存到其它地方。

例如,能够在系统的flash中开出一块空间专门用于打印信息的保存。而后,写一个内核模块,再在die函数中加一个回调函数。这样,每当回调函数被调用,就通知自定义的内核模块,在模块中能够把调用栈还有其它感兴趣的信息保存到那块专用flash空间中去。这里有一点须要注意的是,oops时内核可能不稳定,因此为了确保信息能被正确写入flash,在写flash的函数中尽可能不要用中断,而用轮循的方式。另外信号量、sleep等可能致使阻塞的函数也不要使用。

此外,因为oops时系统还在运行,因此能够发一个消息(信号,netlink等)到用户空间,通知用户空间作一些信息收集工做。

 

Panic

Panic时,Linux处于更最严重的错误状态,标志着整个系统不可用,即中断、进程调度等都已经中止,但栈还没被破坏。因此,oops中的栈回溯理论上仍是能用。printk函数中由于没有阻塞,也仍是可以使用。

用户态call trace

用户程序能够在如下情形call trace,以方便调试:

l         程序崩溃时,都会收到一个信号。Linux系统接收到某些信号时会自动打印call trace。

l         在用户程序中添加检查点,相似于assert机制,若是检查点的条件不知足,就执行call trace

用户态的call trace与内核态相同,一样知足EABI标准,原理以下:

GNU标准中,有一个内建函数__builtin_frame_address。这个函数能够返回当前执行上下文的栈底(Frame Header)指针(同时也是指向Back Chain Word的指针),经过这个指针获得当前调用栈。而这个调用栈中,会有上一级调用函数的栈底指针,经过这个指针再回溯到上一级的调用栈。以此类推完成整个call dump过程。

获得函数的地址后,能够经过符号表获得函数名字。若是是动态库中定义的函数,还能够经过扩展函数dladdr获得这个函数的动态库信息。

相关文章
相关标签/搜索