最近在看Linux USB Composite Framework的内容,经常看到函数指针跳转来跳转去。比如说会看到某个代码执行结构体中的.bind函数指针,但又不知道到底是谁在调用它。
此时,就可以用dump_stack()这个函数来追踪函数调用关系。当然,还是要自己尝试学习理解这个框架结构,不然纯粹的知道函数调用关系意义不大。另外,dump_stack()可用来定位Kernel Panic和Oop的问题,配合objdump和addr2line可以定位到哪一行的哪句代码出现问题。
例子
比如说在以下3个结构体中都包含.bind的成员,都同属于Linux USB Composite Framework的范畴,看多了会不知道谁调用谁。
1  | struct usb_composite_driver {  | 
测试用例
1  | static int composite_bind(struct usb_gadget *gadget,  | 
比如说我在composite_bind()中调用dump_stack(),代码如上。得到的函数调用栈如下:
1  | [ 35.571746] 1111111111111111111  | 
很明显,我们可以通过这个调用栈的信息知道composite_bind()的调用关系(从下往上)如下:
ret_fast_syscall->SyS_init_module->load_module->do_one_initcall->usb_gadget_probe_driver->udc_bind_to_driver->composite_bind->dump_stack->show_stack->unwind_backtrace
第1行是module_init()相关的调用,也就是说调用了module_init()加载某个驱动。更一般的,我们知道是注册一个USB Composite Driver的过程;
第2行可以直观的看到调用composite_bind()是哪个函数;
第3行是dump_stack()的调用关系;
这里只分析如下四条打印语句,从下往上逐条分析。
1  | [ 35.596540] [<c0012288>] (show_stack) from [<c047d8a8>] (dump_stack+0x80/0x90)  | 
反汇编文件
在函数之后的[libcomposite]和[udc_core],标记这是ko文件libcomposite.ko和udc_core.ko。如果没有标记,说明这是build-in的,只需反汇编vmlinux即可。
接下来我们就将这两个ko文件和vmlinux文件objdump出来。在kernel-xxx/目录下执行以下语句进行反汇编。
1  | ../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-objdump -d -l -f -g -S drivers/usb/gadget/libcomposite.ko > composite.log  | 
dump_stack()格式分析
(1)、
1  | [<bf0236a4>] (udc_bind_to_driver [udc_core]) from [<bf023f24>] (usb_gadget_probe_driver+0x70/0xcc [udc_core])  | 
从上面的信息,我们至少可以获得以下信息:
- 这个是编译进
udc-core的ko文件的,因此我们要查看udc-core.log文件; - 在
0xbf023f24的地址(usb_gadget_probe_driver()函数的地址偏移0x70)会调用udc_bind_to_driver()函数。因此我们可以得出usb_gadget_probe_driver()函数的入口地址为0xbf023f24-0x70=0xbf023eb4; usb_gadget_probe_driver()函数总的偏移量为0xcc,即范围为:0xbf023eb4~0xbf023f80
查看udc-core.log文件,搜索”usb_gadget_probe_driver”的关键字,我们可以得到usb_gadget_probe_driver()函数的位置:
1  | 00000eb4 <usb_gadget_probe_driver>:  | 
对于这样格式的内容,表示看不懂。但从”00000eb4 <usb_gadget_probe_driver>:”这句话可以推测usb_gadget_probe_driver()函数在udc-core.ko的入口地址为:0x00000eb4。0x00000eb4与前面推测的0xbf023eb4总是偏移0xbf023000。我猜想,0xbf023000的偏移量正是udc-core.ko相对整个kernel的偏移量。
所以要找到调用udc_bind_to_driver()的地方,那么其偏移量相对于udc-core.ko应为0xf24。
为了找到调用的该函数的所在行,我们使用addr2line工具将地址转换为行号:
1  | #../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-addr2line -e drivers/usb/gadget/udc-core.ko 0xf24  | 
就可以知道在usb_gadget_probe_driver()函数的入口在udc-core.c第625行,调用udc_bind_to_driver()在udc-core.c第643行。查看代码跟解析出来的一致:
(2)、
按照同样的方法再来解析下面的log:
1  | [<bf045f60>] (composite_bind [libcomposite]) from [<bf0236a4>] (udc_bind_to_driver+0x50/0x110 [udc_core])  | 
- 这个是编译进
udc-core的ko文件的,因此我们要查看udc-core.log文件; - 在
0xbf0236a4(在udc-core.ko的地址为0x000006a4)的地址(偏移0x50)会调用composite_bind()函数。因此我们可以得出udc_bind_to_driver()函数的地址为0xbf0236a4-0x50=0xbf023654(在udc-core.ko的地址为0x00000654); udc_bind_to_driver()函数总的偏移量为0x110,即范围为:0xbf023654~0xbf023764;1
2
3
4
5#../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-addr2line -e drivers/usb/gadget/udc-core.ko 0x6a4
/home/victor/work/xxx_project/kernel-xxx/drivers/usb/gadget/udc-core.c:577
#../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-addr2line -e drivers/usb/gadget/udc-core.ko 0x654
/home/victor/work/xxx_project/kernel-xxx/drivers/usb/gadget/udc-core.c:566
就可以知道udc_bind_to_driver()函数入口在udc-core.c第566行,调用composite_bind()函数在udc-core.c第577行。
(3)、
1  | [ 35.603793] [<c047d8a8>] (dump_stack) from [<bf045f60>] (composite_bind+0x28/0x1b0 [libcomposite])  | 
- 这个是编译进
libcomposite的ko文件的,因此我们要查看libcomposite.log文件; 查看
composite.log文件并搜索”composite_bind”得到其地址为0x00002f38,因此我们可以知道libcomposite.ko相对整个kernel偏移0xbf045f38-0x00002f38=0xbf043000。1
2
3
4
500002f38 <composite_bind>:
composite_bind():
/home/victor/work/xxx_project/kernel-xxx/drivers/usb/gadget/composite.c:1671
device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
}以
libcomposite.ko的地址为基准,在composite_bind()函数起始地址0x00002f38中偏移0x28,即0x00002f60会去调用dump_stack()函数。1
2
3
4
5#../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-addr2line -e drivers/usb/gadget/libcomposite.ko 0x2f60
/home/victor/work/xxx_project/kernel-xxx/drivers/usb/gadget/composite.c:1677
#../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-addr2line -e drivers/usb/gadget/libcomposite.ko 0x2f38
/home/victor/work/xxx_project/kernel-xxx/drivers/usb/gadget/composite.c:1671
dump_stack()的位置,追本溯源终于找到自己熟悉的地方了。(4)、
1  | [ 35.596540] [<c0012288>] (show_stack) from [<c047d8a8>] (dump_stack+0x80/0x90)  | 
这些是build-in的,直接在vmlinux.ko就可以找到他们的地址。
1  | #../prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-addr2line -e vmlinux 0xc047d8a8  | 
参考资料
http://einon.net/DocBook/kernel-api/API-vsnprintf.html
http://blog.csdn.net/liyongming1982/article/details/16349769
http://blog.csdn.net/liyongming1982/article/details/16349875
http://blog.csdn.net/jasonchen_gbd/article/details/45585133