dump_stack()函数的使用,相信很多做kernel开发的人都有接触过,这个kernel函数在分析代码流程上是非常有帮忙的,在内核日志中打印当前CPU的调用栈信息,不需要任何的参数,直接在所需要的代码段加入dump_stack(),调用它会立即打印可以协助开发人员快速了解到代码的流程,从而快速分析问题。
直接展示demo的分析过程:
在上面的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] [
[ 6.106043] [
[ 6.111380] [
[ 6.116542] [
[ 6.122314] [
[ 6.127481] [
[ 6.133169] [
[ 6.139116] [
[ 6.145328] [
[ 6.150845] [
[ 6.156703] [
[ 6.161871] [
[ 6.167382] [
[ 6.173332] [
[ 6.179455] [
[ 6.185315] [
[ 6.192053] [
[ 6.198442] [
[ 6.204652] [
[ 6.210692] [
[ 6.216114] [
[ 6.220931] [
[ 6.226264] [
[ 6.232128] [
[ 6.237907] [
[ 6.243508] [
[ 6.249370] [
[ 6.255226] [
[ 6.260743] [
[ 6.266340] [
[ 6.271940] [
[ 6.277715] [
[ 6.283935] [
[ 6.289708] [
[ 6.295496] [
[ 6.301876] [
[ 6.307114] [
[ 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选项才能正常工作