开发内核模块时,个人认为kdump+crash是必备的工具,kdump用于在内核崩溃时生成转储文件(core文件),crash用来分析core文件,查看崩溃时的栈信息、调用信息、出错的执行路径等。
如果出错的位置是在内核函数中(当然大部分是由于模块不正确地调用内核函数),则很容易看到内核的代码位置。假设出错的函数是blk_requeue_request,出错的位置在blk_requeue_request函数中的偏移为82,在crash中输入“gdb L*blk_requeue_request+82”即可查看出错位置对应的源代码,如下图所示:
当然也可以通过“dis -l blk_requeue_request+82”或者“dis -l 地址”(地址为RIP的值)也可以看到,但是没有上面的命令直观,总之是很容易看到的,然后再根据业务逻辑很容易就能找到错误。
如果出错的位置是在内核模块中,就很悲剧了,因为即使在编译的时候加上调试信息,也没有办法看到。当然将模块和内核一起编译,可能会看到,但是内核模块在使用时很少和内核一起编译,而且调试的时候每次都重新编译内核也很烦啊。google了很久,也问了很多人,都没找到什么办法。最后自己摸索了半天,找到一种比较麻烦,但是个人感觉还是挺有用的一个方法,步骤如下:
1、编译时增加debug选项
两种方式,一种是在make的时候增加COPTS=-g,另一种方法是使用alias命令将“gcc -g”作为gcc的别名。
2、找到出错的汇编代码
使用crash读取core文件,首先输入bt命令,会显示如下信息(仅作为参考):
从这里可以看出我的出错函数是fcluster_skb_split,偏移是216,RIP的值为ffffffffa03bda68。
然后输入“dis -l fcluster_skb_split+216”,就可以看到出错时的汇编语句,如下图所示:
至此,第二步完成了
3、找到对应的源代码
首先使用“objdump -S fcluster_skb.o >tmpfile”,其中fcluster_skb.o是fcluster_skb_split函数所在的目标文件;输出最后存到一个临时文件中,便于搜索。然后在tmpfile中搜索出错的汇编语句(我这里的是“movzbl 0xd(%rsi),%eax”),如下图所示:
这样就可以找到出错的汇编语句对应的源代码了。当然如果搜索到多处相同的汇编代码,那就得结合bt命令的输出和业务逻辑来确定出错的代码了。至此终于找到了出错的位置了。
当然,对于汇编高手来说,根本不用这么曲折,以后还是得了解一下汇编啊!如果还有更好的方法,欢迎交流!
阅读(4122) | 评论(0) | 转发(1) |