linux内核调试技巧 dump_stack()

本文探讨了Linux内核dump_stack函数如何用于函数调用跟踪,通过实例展示了如何在read函数中使用它。同时,指出了dump_stack可能存在的不足,如内核函数范围限制、编译优化影响和堆栈中的误导信息。
摘要由CSDN通过智能技术生成

linux内核提供函数dump_stack()来跟踪函数的调用过程,原理是通过打印当前cpu的堆栈的调用函数来显示当前的上下文环境与调用关系;

例:

创建一个混杂设备,并定义read函数。在read中加入dump_stack(),显示read调用关系。结果如下:

<1>[  950.351269] --------------[fyl] dump_stack start----------------
[ 1560.465491] CPU: 0 PID: 2759 Comm: cat Tainted: GF          O 3.13.0-32-generic #57-Ubuntu
[ 1560.465494] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015
[ 1560.465496]  00000000 00000000 ddf47f58 c1650cd3 dc05e3c0 ddf47f64 e0937010 00000000
[ 1560.465502]  ddf47f88 c1179238 ddf47f98 ddf47fac c1125645 00a000c9 dc05e3c0 00000000
[ 1560.465506]  09431000 ddf47fac c11799a9 ddf47f98 00010000 00000000 00000000 00000003
[ 1560.465511] Call Trace:
[ 1560.465521]  [<c1650cd3>] `dump_stack`+0x41/0x52
[ 1560.465525]  [<e0937010>] `my_read`+0x10/0x20 [test]
[ 1560.465529]  [<c1179238>] `vfs_read`+0x78/0x140
[ 1560.465534]  [<c1125645>] ? `SyS_fadvise64_64`+0x1e5/0x260
[ 1560.465537]  [<c11799a9>] `SyS_read`+0x49/0x90
[ 1560.465542]  [<c165efcd>] `sysenter_do_call`+0x12/0x28
<1>[  950.642678] --------------[fyl] dump_stack end--------------

从下往上的显示调用顺序关系。

test.c

#include <linux/kprobes.h>
#include <asm/traps.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
 
int my_read(struct file *file, char __user *user, size_t size, loff_t * loff)
{
		printk(KERN_INFO "dump_stack start\n");
        dump_stack();
        printk(KERN_INFO "dump_stack end\n");
        return 0;
}
 
int my_open(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "This is miscdevice open\n");
        return 0;
}
 
int my_close(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "This is miscdevice close\n");
        return 0;
}
 
const struct file_operations my_fops = {
        .open = my_open,
        .release = my_close,
        .read = my_read,
};
 
struct miscdevice  my_misc = {
        .minor = 201,
        .name = "stone",
        .fops = &my_fops,
};
 
static __init int hello_init(void)
{
        misc_register(&my_misc);
        printk(KERN_ALERT "helloworld!\n");
        return 0;
}
 
static __exit void hello_exit(void)
{
        misc_deregister(&my_misc);
        printk(KERN_ALERT "helloworld exit!\n");
}
 
module_init(hello_init);
module_exit(hello_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stone");

但是,使用dump_stack有不足之处

dump_stack的原理是遍历堆栈,把所有可能是内核函数的内容找出来,并打印对应的函数。因为函数调用时会把下一条指令的地址放到堆栈中。所以只要找到这些return address,就可以找到这些return address所在函数,进而打印函数的调用关系。

使用dump_stack可能不准确,可能的原因有三:
1.所有这些可以找到的函数地址,存在/proc/kallsyms中。它并不包括内核中所有的函数,而只包括内核中stextetext和sinittexteinittext范围的函数,及模块中的函数。详细可参考scripts/kallsyms.c
2.一些函数在编译时进行了优化,把call指令优化为jmp指令,这样在调用时就不会把return address放到堆栈,导致dump_stack时在堆栈中找不到对应的信息。
3.堆栈中可能有一些数值,它们不是return address,但是在内核函数地址的范围里,这些数值会被误认为return address从而打印出错误的调用关系。

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值