最近在看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