kernel 调试技巧之dump_stack()分析函数调用关系

        dump_stack()函数的使用,相信很多做kernel开发的人都有接触过,这个kernel函数在分析代码流程上是非常有帮忙的,在内核日志中打印当前CPU的调用栈信息,不需要任何的参数,直接在所需要的代码段加入dump_stack(),调用它会立即打印可以协助开发人员快速了解到代码的流程,从而快速分析问题。

直接展示demo的分析过程:

kernel 调试技巧之dump_stack()分析函数调用关系_第1张图片

在上面的kernel的驱动代码中加入,即可在开机的内核打印中获取如下:

6.078334] [xxx-dump] in xdma_device_open, line = 4167, dump start
[    6.085156] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.9.253-tegra #244
[    6.091773] Hardware name: NVIDIA Jetson Xavier NX Developer Kit (DT)
[    6.098338] Call trace:
[    6.100794] [] dump_backtrace+0x0/0x198
[    6.106043] [] show_stack+0x24/0x30
[    6.111380] [] +0xa0/0xc4
[    6.116542] [] xdma_device_open+0x74/0xda8
[    6.122314] [] probe_one+0xe8/0x300
[    6.127481] [] pci_device_probe+0xa4/0x140
[    6.133169] [] driver_probe_device+0x298/0x448
[    6.139116] [] __device_attach_driver+0xa8/0x148
[    6.145328] [] bus_for_each_drv+0x58/0xa8
[    6.150845] [] __device_attach+0xc8/0x158
[    6.156703] [] device_attach+0x24/0x30
[    6.161871] [] pci_bus_add_device+0x58/0x98
[    6.167382] [] pci_bus_add_devices+0x40/0x90
[    6.173332] [] pci_bus_add_devices+0x88/0x90
[    6.179455] [] dw_pcie_host_init+0x2ec/0x530
[    6.185315] [] tegra_pcie_dw_runtime_resume+0x1bc/0x370
[    6.192053] [] pm_generic_runtime_resume+0x3c/0x58
[    6.198442] [] __genpd_runtime_resume+0x38/0xa0
[    6.204652] [] genpd_runtime_resume+0xa4/0x210
[    6.210692] [] __rpm_callback+0x74/0xa0
[    6.216114] [] rpm_callback+0x34/0x98
[    6.220931] [] rpm_resume+0x470/0x710
[    6.226264] [] __pm_runtime_resume+0x4c/0x70
[    6.232128] [] tegra_pcie_dw_probe+0x8d8/0xbb0
[    6.237907] [] platform_drv_probe+0x60/0xc0
[    6.243508] [] driver_probe_device+0x298/0x448
[    6.249370] [] __driver_attach+0xdc/0x128
[    6.255226] [] bus_for_each_dev+0x5c/0xa8
[    6.260743] [] driver_attach+0x30/0x40
[    6.266340] [] bus_add_driver+0x20c/0x2a8
[    6.271940] [] driver_register+0x6c/0x110
[    6.277715] [] __platform_driver_register+0x5c/0x68
[    6.283935] [] tegra_pcie_rp_init+0x18/0x20
[    6.289708] [] do_one_initcall+0x44/0x130
[    6.295496] [] kernel_init_freeable+0x1a0/0x244
[    6.301876] [] kernel_init+0x18/0x108
[    6.307114] [] ret_from_fork+0x10/0x30
[    6.312763] [xxx-dump] in xdma_device_open, line = 4169, dump end

接着代码该如何寻找流程:

Step1:


void *xdma_device_open(const char *mname, struct pci_dev *pdev, int *user_max, int *h2c_channel_max, int *c2h_channel_max)
{
        struct xdma_dev *xdev = NULL;
        int rv = 0;

        pr_info("%s device %s, 0x%p.\n", mname, dev_name(&pdev->dev), pdev);

        printk("[xxx-dump] in %s, line = %d, dump start \n", __func__, __LINE__);
        dump_stack();
        printk("[xxx-dump] in %s, line = %d, dump end \n", __func__, __LINE__);
        /* allocate zeroed device book keeping structure */
        xdev = alloc_dev_instance(pdev);
    
        ...
free_xdev:
        kfree(xdev);
        return NULL;
}

先在添加dump_stack()中寻找调用的函数xdma_device_open()

Step2:

static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
        int rv = 0;
        struct xdma_pci_dev *xpdev = NULL;
        struct xdma_dev *xdev;
        void *hndl;
        unsigned int reg_addr_offset;
        unsigned reg_val;
        unsigned idx;

        printk("xdma ** probe_one \n");
        cameraPcieDev = xpdev_alloc(pdev);
        if (!cameraPcieDev)
                return -ENOMEM;


        printk("%s  user_max = %d  h2c_channel_max = %d  c2h_channel_max = %d\n",__func__,cameraPcieDev->user_max,cameraPcieDev->h2c_channel_max,cameraPcieDev->c2h_channel_max);

        hndl = xdma_device_open(DRV_MODULE_NAME, pdev, &cameraPcieDev->user_max,
                        &cameraPcieDev->h2c_channel_max, &cameraPcieDev->c2h_channel_max);
        if (!hndl) {
                rv = -EINVAL;
                goto err_out;
        }
        
        ....
        
        return 0;

err_out:
        pr_err("pdev 0x%p, err %d.\n", pdev, rv);
        xpdev_free(cameraPcieDev);
        return rv;
}

接着找到在probe_one() 函数中调用的调用的函数xdma_device_open()的

Step3:

static struct pci_driver pci_driver = {
        .name = DRV_MODULE_NAME,
        .id_table = pci_ids,
        .probe = probe_one,
        .remove = remove_one,
        .err_handler = &xdma_err_handler,
};

int xdma_mod_init(void)
{
        int rv;

        pr_info("%s", version);

        printk("xdma_mod_init ==\n");
        if (desc_blen_max > XDMA_DESC_BLEN_MAX)
                desc_blen_max = XDMA_DESC_BLEN_MAX;
        pr_info("desc_blen_max: 0x%x/%u, timeout: h2c %u c2h %u sec.\n",
                desc_blen_max, desc_blen_max, h2c_timeout, c2h_timeout);

        rv = xdma_cdev_init();
        if (rv < 0)
                return rv;

        return pci_register_driver(&pci_driver);
}




接着在xdma_mod_init(void) 函数中启动pci_register_driver(&pci_driver);而pci_driver的结构体中就有.probe = probe_one 成员启动

Step4: 

extern int xdma_mod_init(void);

static int opt_pcie_probe(struct platform_device *pdev)
{


        int val = -1;
        int try_cnt = 0;
        int gpio_val = -1;
        int ret = -1;
        int irq_cap;
        opt_pcie = kmalloc(sizeof(struct OPT_PCIE),GFP_KERNEL);
        memset(opt_pcie,0,sizeof(struct OPT_PCIE));

        printk("enter %s line = %d. \n", __func__, __LINE__);
        opt_pcie->nd = of_find_node_by_path("/xilinx_xdma");

        if(opt_pcie->nd == NULL) {
                //printk("[hezhensheng] gpio node cant not found!\r\n");
                return -EINVAL;
        }

        //xdma_mod_init();
#if 1

        opt_pcie->pcie_status = of_get_named_gpio(opt_pcie->nd, "gpio-link_up", 0);//get pcie detect gpio
        printk("%s, pcie_status gpio = %d \n", __func__, opt_pcie->pcie_status);
        if (!gpio_is_valid(opt_pcie->pcie_status)){
                printk("pcie_status : %d\n",opt_pcie->pcie_status);
                return -1;
        }

        ret = gpio_request(opt_pcie->pcie_status, "opt_xdma_pcie_status");
         if( ret != 0){
                printk("[hezhensheng] gpio_request core_info->reset_gpio invalid: %d\n",opt_pcie->pcie_status);
                return ret;
        }
        gpio_direction_input(opt_pcie->pcie_status);
        gpio_val = gpio_get_value(opt_pcie->pcie_status);
        printk("opt_pcie_probe get gpio status (opt_xdma_pcie_status) opt_pcie->pcie_status = %d \r\n", gpio_val);
//add by tab for 2024-10-31 detect fpga boot up ok
retry_read:
        gpio_val = gpio_get_value(opt_pcie->pcie_status);
        printk("opt_pcie_probe get opt_pcie->pcie_status = %d \r\n",gpio_val);
        if(gpio_val == 1) //if(gpio_val == 0) old default
        {
                printk("opt_pcie_probe get pcie status val 111= %d \n",gpio_val);
                val = xdma_mod_init();
                if(val != 0)
                {
                         msleep(100);
                         goto retry_read;
                }
        
        ....
        
        return 0;
}
static const struct of_device_id opt_pcie_match[] = {
    { .compatible = "xilinx_fpga_xdma" },
    { /* Sentinel */ }
};

static struct platform_driver opt_pcie_init_driver = {
    .driver = {
        .name = "xilinx_fpga_xdma",
        .of_match_table = opt_pcie_match,
    },
    .probe = opt_pcie_probe,
    .remove = opt_pcie_remove,
};

module_platform_driver(opt_pcie_init_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("opt");


opt_pcie_probe函数中启动xdma_mod_init(void) 函数

注意事项:

        1.dump_stack()的输出会出现在内核日志中,可能会影响性能

        2.在量产软件环境中慎重,最好是在开发debug阶段使用

        3.输出的栈信息可能因为编译器优化而不完全准确

        4.需要在内核配置中CONFIG_STACKTRACE选项才能正常工作

你可能感兴趣的:(linux,驱动开发,嵌入式硬件)