分类: LINUX
2014-08-19 10:31:35
C语言的函数调用,是通过栈来实现的。如下图所示:
函数调用栈
内核异常或死机时,经常在内核日志中看到打印的栈信息和寄存器值。从函数栈信息,我们可以知道函数间的调用关系,从栈和寄存器数据,还可以得到各个变量、参数的值。对Linux内核理解和故障定位非常有帮助。
若希望打印出栈信息的函数中加上dump_stack()即可,dump_stack()已导出到内核符号表中,可直接调用。
00276: / *
00277: * The architecture- independent dump_stack generator
00278: */
00279: void dump_stack(void)
00280: {
00281: unsigned long stack;
00282:
00283: show_trace(current, NULL, &stack);
00284: }
00285:
00286: EXPORT_SYMBOL(dump_stack);
00287:
若是内核代码,则需要重新编译内核。若是内核模块,编译后,重新加载该模块。注意要控制好打印栈信息的次数和频度。内核中有些代码是大量被执行的,这时要控制打印次数,否则系统长时间无法响应。
下面栈信息是在文件fs/jbd/transaction.c,函数journal_dirty_data()中加入dump_stack()函数后的获取到的栈信息。
对于Linux内核栈信息来说,也主要是函数的调用关系。
如下面栈信息:
对于语句:
[
因为是最后一个函数,c03743c8是dump_stack()函数的地址。
linux:~ # cat /proc/kallsyms |grep smp_scan_config
c037431f t smp_scan_config
通过查找内核符号表,我们看到smp_scan_config()函数在内核中的地址是c037431f。
“+0xa9/0xcb”中0xa9含义是当前函数在上一层函数内的偏移量。对于本句来说,就是dump_stack()函数在smp_scan_config()内核的偏移量为0xa9,行首打印的地址是c037431f + a9 = c03743c8 。“0xcb”表示smp_scan_config()函数代码大小为0xcb。
同理,我们知道在find_smp_config()函数中调用了smp_scan_config()函数,调用该函数的位置是代码c037441c处,偏移量为0x32。find_smp_config()函数大小为0x5c。
打印内核栈信息的原理是:将栈中的值都作地址对待,然后根据地址,首先查看该地址是否属于内核代码(text)段或初始代码(inittext)段中的函数。若是,则打印函数信息。
if (is_kernel_text(addr) || is_kernel_inittext(addr))
00165: const char *kallsyms_lookup(unsigned long addr,
00166: unsigned long *symbolsize,
00167: unsigned long *offset,
00168: char **modname, char *namebuf)
… …
00179: if ((all_var && is_kernel(addr)) ||
00180: (! all_var && (is_kernel_text(addr) ||
00181: is_kernel_inittext(addr)|| is_kernel_extratext(addr)))) {
… …
若不是内核代码(text)段或初始代码(inittext)段中的函数,那么进而查找是为模块(驱动)中的函数。若是,则打印函数信息。
00224: / * see if it's in a module */
00225: msym = module_address_lookup(addr, symbolsize, offset,
modname);
00226: if (msym)
00227: return strncpy(namebuf, msym, KSYM_NAME_LEN);
若既不是内核代码(text)段或初始代码(inittext)段中的函数,也不是模块(驱动)中的函数;那么什么信息都不打印,继续取栈中的值,直到栈底。