Linux内核动态调试信息的打开,如pr_debug

本文详细介绍如何在Linux内核中使用pr_debug进行动态调试,包括配置Kconfig和Makefile支持调试信息,设置内核参数以启用动态printk支持等步骤。通过具体实例展示按键驱动模块的调试过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


【1】配置Kconfig,添加要调试驱动模块的DEBUG选项
如在driver/char目录下的Kconfig中添加如下代码:
  1. config MY_KEY //该部分配置驱动是否编译进内核/编译为内核模块/不编译
  2.     tristate "Key driver for FriendlyARM Tiny6410 development boards"
  3.     depends on CPU_S3C6410
  4.     default m
  5.     help
  6.      this is buttons driver, add by Jason

  7. config MY_KEY_DEBUG //该部分是调试时添加,配置是否打印调试信息,如果不需要可不添加
  8.     bool "Support MY_KEY DEBUG"
  9.     help
  10.      this is DEBUG function, add by Jason
  11. depends on MY_KEY

【2】配置Makefile,添加编译支持驱动模块 EXTRA_CFLAGS += -DDEBUG 
如在driver/char目录下的Makefile中添加如下代码:
  1. obj-$(CONFIG_MY_KEY)        += my_key.o
  2. ifeq ($(CONFIG_MY_KEY_DEBUG), y)             //判断是否调试驱动
  3.     EXTRA_CFLAGS += -DDEBUG
  4. endif

【3】配置内核,使支持动态调试
Kernel hacking  --->
[*] Tracers  --->
[*] Trace max stack
Kernel hacking  --->
[*] Enable dynamic printk() support


【4】如果驱动选择的是动态加载,则重新编译模块(make modules);否则,重新编译内核(make uImage -j4)。

【5】如果驱动选择的是动态加载,则传*.ko文件到板子的文件系统;否则,重烧内核。然后改变控制台debug消息显示级别7-->8,可以打印printk(DEBUG ...)信息。

【6】驱动源码:蓝绿色部分是pr_debug,有了这个,就可以在配置内核时通过选择或取消DEBUG选项,控制调试信息的开和关;绿色部分是在代码里通过修改DRIVER_DEBUG的值,控制调试信息的开和关,相比较,很显然是pr_debug比较方便。
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>
  4. #include <linux/fs.h>
  5. #include <linux/errno.h>
  6. #include <linux/cdev.h>
  7. #include <linux/types.h>
  8. #include <linux/device.h>
  9. #include <linux/mm.h>
  10. #include <linux/slab.h>
  11. #include <linux/sched.h>
  12. #include <linux/poll.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/irq.h>
  15. #include <asm/uaccess.h>
  16. #include <asm/irq.h>
  17. #include <asm/io.h>
  18. #include <asm/system.h>
  19. #include <mach/map.h>
  20. #include <mach/regs-clock.h>
  21. #include <mach/regs-gpio.h>
  22. #include <mach/irqs.h>
  23. #include <mach/gpio-bank-n.h>
  24. #include <plat/gpio-cfg.h>

  25. #define DRIVER_DEBUG    1
  26. #define DEVICE_NAME        "mykey"

  27. /* parameters */

  28. static int major;
  29. static int minor = 0;
  30. static int nr_devs = 1;            /* number of devices */

  31. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

  32. static struct fasync_struct *button_async;

  33. /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
  34. static volatile int ev_press = 0;

  35. /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
  36. /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
  37. struct pin_desc{
  38.     unsigned int pin_num;
  39.     unsigned int key_val;
  40. };

  41. struct pin_desc pins_desc[4] = {
  42.     {0x00, 0x01},
  43.     {0x01, 0x02},
  44.     {0x02, 0x03},
  45.     {0x03, 0x04},
  46. };

  47. static unsigned char key_val;

  48. struct class *mykey_class;

  49. struct mykey_dev {
  50.     struct cdev cdev;        /* char device structure */
  51. }*mykey_devp;            /* device structure pointer */

  52. /*
  53.  * 确定按键值
  54.  */
  55. static irqreturn_t buttons_irq(int irq, void *dev_id, struct pt_regs *regs)
  56. {
  57.     struct pin_desc * pindesc = (struct pin_desc *)dev_id;
  58.     unsigned int pinval;

  59.     pr_debug("In the %s function!\n", __FUNCTION__);

  60.     pinval = (~readl(S3C64XX_GPNDAT)) & (<< pindesc->pin_num);

  61.     if (pinval)    //one of the key is pressed
  62.         key_val = pindesc->key_val;
  63.     else
  64.         key_val = 0x80 | pindesc->key_val;

  65.     ev_press = 1;         /* 表示中断发生了 */

  66.     wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */

  67.     kill_fasync (&button_async, SIGIO, POLL_IN);

  68.     return IRQ_RETVAL(IRQ_HANDLED);
  69. }


  70. /*
  71.  * The fasync function of device driver.
  72.  */
  73. static int mykey_fasync (int fd, struct file *filp, int on)
  74. {
  75.     pr_debug("driver: mykey_fasync!\n");

  76.     return fasync_helper (fd, filp, on, &button_async);
  77. }


  78. /*
  79.  * The open function of device driver.
  80.  */

  81. static int mykey_open(struct inode *inode, struct file *filp)
  82. {
  83.     pr_debug("In the %s function!\n", __FUNCTION__);

  84.     request_irq(IRQ_EINT(0), buttons_irq, IRQ_TYPE_EDGE_BOTH, "S1", &pins_desc[0]);
  85.     request_irq(IRQ_EINT(1), buttons_irq, IRQ_TYPE_EDGE_BOTH, "S2", &pins_desc[1]);
  86.     request_irq(IRQ_EINT(2), buttons_irq, IRQ_TYPE_EDGE_BOTH, "S3", &pins_desc[2]);
  87.     request_irq(IRQ_EINT(3), buttons_irq, IRQ_TYPE_EDGE_BOTH, "S4", &pins_desc[3]);

  88.     return 0;
  89. }


  90. /*
  91. * The release function of device driver.
  92. */
  93. static int mykey_release(struct inode *inode, struct file *filp)
  94. {
  95.     pr_debug("In the %s function!\n", __FUNCTION__);

  96.     free_irq(IRQ_EINT(0), &pins_desc[0]);
  97.     free_irq(IRQ_EINT(1), &pins_desc[1]);
  98.     free_irq(IRQ_EINT(2), &pins_desc[2]);
  99.     free_irq(IRQ_EINT(3), &pins_desc[3]);

  100.     return 0;
  101. }


  102. /*
  103. * The read function of device driver. 
  104. */
  105. static int mykey_read(struct file *filp, char __user *buff,
  106.         size_t count, loff_t *offp)
  107. {
  108.     pr_debug("In the %s function!\n", __FUNCTION__);

  109.     /* 如果没有按键动作, 休眠 */
  110.     wait_event_interruptible(button_waitq, ev_press);

  111.     pr_debug("Starting to read data in the %s function!\n", __FUNCTION__);

  112.     /* 如果有按键动作, 返回键值 */
  113.     copy_to_user(buff, &key_val, 1);

  114.     ev_press = 0;

  115.     return 1;
  116. }


  117. /*
  118.  * The file operations for the device
  119.  */
  120. static struct file_operations mykey_fops = {
  121. .owner = THIS_MODULE,
  122. .open = mykey_open,
  123. .release = mykey_release,
  124. .read = mykey_read,
  125. .fasync = mykey_fasync,
  126. };


  127. /*
  128.  * Set up a cdev entry.
  129.  */
  130. static void mykey_setup_cdev(struct mykey_dev *dev, int index)
  131. {
  132.     int err, devno;

  133.     devno= MKDEV(major, minor + index);
  134.     cdev_init(&dev->cdev, &mykey_fops);
  135.     dev->cdev.owner = THIS_MODULE;
  136.     err = cdev_add (&dev->cdev, devno, 1);
  137.     if (err)
  138.         printk(KERN_NOTICE "Error %d adding mykey%d", err, index);
  139. }


  140. /*
  141.  * Initialize the pipe devs; return how many we did.
  142.  */
  143. static int __init mykey_init_module(void)
  144. {
  145.     int ret;

  146.     dev_t devno = MKDEV(major, minor);
  147.      
  148.     pr_debug("In the %s function!\n", __FUNCTION__);

  149.     if (major)
  150.         ret = register_chrdev_region(devno, nr_devs, DEVICE_NAME);
  151.     else {
  152.         ret = alloc_chrdev_region(&devno, minor, nr_devs, DEVICE_NAME);
  153.         major = MAJOR(devno);
  154.     }
  155.     if (ret < 0) {
  156.         printk(KERN_WARNING "%s: can't get major %d\n", \
  157.             DEVICE_NAME, major);
  158.         return ret;
  159.     }

  160.     pr_debug("After \"alloc_chrdev_region()\"\n");

  161.     mykey_devp = kzalloc(sizeof(struct mykey_dev), GFP_KERNEL);
  162.     if (!mykey_devp) {
  163.         ret = - ENOMEM;
  164.         goto fail_malloc;
  165.     }

  166.     pr_debug("After \"kzalloc()\"\n");

  167.     /* The following step must after kmalloc() and memset() */
  168.     mykey_setup_cdev(mykey_devp, 0);

  169.     pr_debug("After \"mykey_setup_cdev()\"\n");

  170.     /* 1. create your own class under /sys
  171.     * 2. register your own device in sys
  172.     * this will cause udev to create corresponding device node 
  173.     */
  174.     mykey_class = class_create(THIS_MODULE, "mykey_class");
  175.     if (IS_ERR(mykey_class)) {
  176.         printk(DEVICE_NAME " failed in creating class./n");
  177.         return -1; 
  178.     }
  179.     device_create(mykey_class, NULL, devno, NULL, "mykey""%d", 0);

  180.     pr_debug("After \"class_create()\" and \"device_create\"\n");

 #ifdef DRIVER_DEBUG
  1. printk(DEVICE_NAME "\tinitialized, major = %d, minor = %d.\n",
  2.          major, minor);
  3. #endif
  1.     return 0;

  2. fail_malloc:
  3.     unregister_chrdev_region(devno, 1);

  4.     return ret;
  5. }


  6. /*
  7.  * This is called by cleanup_module or on failure.
  8.  */

  9. static void __exit mykey_exit_module(void)
  10. {
  11.     pr_debug("In the %s function!\n", __FUNCTION__);

  12.     device_destroy(mykey_class, MKDEV(major, minor));
  13.     class_destroy(mykey_class);
  14.     cdev_del(&mykey_devp->cdev);
  15.     kfree(mykey_devp);
  16.     unregister_chrdev_region(MKDEV(major, minor), 1);    

  #ifdef DRIVER_DEBUG
  1.     printk(DEVICE_NAME "\texited!\n");
  2.  #endif
  3. }

  4. module_init(mykey_init_module);
  5. module_exit(mykey_exit_module);

  6. MODULE_AUTHOR("Jason Lu");
  7. MODULE_VERSION("0.1.0");
  8. MODULE_DESCRIPTION("Jason's blog: http://blog.chinaunix.net/space.php?\
  9.         uid=20746260");
  10. MODULE_LICENSE("Dual MPL/GPL");

【7】以下是【3】里Support MY_KEY DEBUG未选中时的运行情况。

【8】keytest.c源码
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <poll.h>
  6. #include <signal.h>
  7. #include <sys/types.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>

  10. int fd;

  11. void my_signal_fun(int signum)
  12. {
  13.     unsigned char key_val;
  14.     read(fd, &key_val, 1);
  15.     printf("key_val: 0x%x\n", key_val);
  16. }

  17. int main(int argc, char **argv)
  18. {
  19.     int Oflags;

  20.     signal(SIGIO, my_signal_fun);
  21.     
  22.     fd = open("/dev/mykey0", O_RDWR);
  23.     if (fd < 0)
  24.     {
  25.         printf("can't open!\n");
  26.     }

  27.     fcntl(fd, F_SETOWN, getpid());    
  28.     Oflags = fcntl(fd, F_GETFL);     
  29.     fcntl(fd, F_SETFL, Oflags | FASYNC);

  30.     while (1)
  31.     {
  32.         sleep(1000);
  33.     }
  34.     
  35.     return 0;
  36. }
<think>好的,我现在要解决的问题是:如何在Android内核中启用pr_debug的输出。用户可能是一名Android开发者或者内核开发者,他们遇到了调试问题,希望通过pr_debug来输出调试信息,但发现默认情况下这些信息没有显示出来。需要找出正确的方法来开启这些调试日志。 首先,我得回忆一下pr_debug的工作原理。pr_debugLinux内核中通常是通过动态调试(dynamic debug)机制控制的,也就是说,默认情况下这些信息不会被打印到控制台或者日志中,除非明确启用。这可能涉及到内核配置选项、动态调试控制文件以及编译时的调试选项。 用户可能已经尝试过直接使用pr_debug,但发现没有输出,所以想知道如何正确配置。需要分步骤讲解,包括内核配置、编译选项、运行时启用方法,以及可能的注意事项。 首先,检查内核配置是否启用了CONFIG_DYNAMIC_DEBUG。这个选项是动态调试的基础,必须在内核配置中打开。用户可能需要进入内核源码目录,使用make menuconfig或者检查.config文件中的相关选项。 其次,编译内核时需要确保调试符号没有被优化掉。可能涉及到CONFIG_DEBUG_KERNEL和CONFIG_DEBUG_INFO选项,这些选项确保调试信息被包含在内核映像中。此外,pr_debug可能依赖于特定的宏定义,比如在包含printk.h之前定义DEBUG宏,或者在编译时通过DDEBUG标志启用。 然后,运行时控制。动态调试允许在运行时通过/sys/kernel/debug/dynamic_debug/control文件来控制哪些pr_debug语句被激活。例如,使用echo命令来启用特定文件的调试输出,或者使用通配符来启用所有。 另外,可能需要调整内核printk日志级别,因为即使pr_debug被激活,如果日志级别设置过高,消息可能不会被显示。printk的日志级别有0到7,其中7是DEBUG级别,而默认的控制台日志级别可能较低,需要调整。 还有,可能需要使用dmesg命令来查看内核日志,或者确保日志缓冲区足够大,以便捕获调试信息。此外,不同版本的内核可能在配置上有所不同,尤其是Android内核可能有一些定制化的部分,需要注意是否有特定的补丁或配置选项。 用户可能遇到的问题包括:配置选项未正确开启、编译时未包含调试信息、运行时未正确激活动态调试、日志级别不够低、或者权限问题无法访问debugfs。需要逐一检查这些可能性,并给出具体的命令和步骤。 总结步骤应该是:配置内核开启动态调试和相关调试选项,重新编译并刷入内核,然后在运行时通过dynamic_debug接口启用特定的pr_debug输出,调整printk日志级别,最后使用dmesg查看输出。同时提醒用户注意内核版本差异和权限问题。</think>在 Android 内核中启用 `pr_debug` 输出需要多步骤配置,以下是详细操作指南: --- ### 一、内核配置准备 #### 1. 开启动态调试支持 ```bash # 在内核源码目录执行 make menuconfig # 定位配置项 Kernel hacking -> -> printk and dmesg options [*] Enable dynamic printk() support (CONFIG_DYNAMIC_DEBUG) [*] Debugging data visibility (选择合适调试级别) ``` #### 2. 关键配置项验证 检查 `.config` 文件需包含: ```ini CONFIG_DYNAMIC_DEBUG=y # 动态调试核心支持 CONFIG_DEBUG_FS=y # debugfs 文件系统支持 CONFIG_DEBUG_INFO=y # 保留调试符号 ``` --- ### 二、编译时调试启用 #### 1. 源文件级调试激活 在需要调试的 C 文件顶部添加: ```c #define DEBUG // 必须在包含 printk.h 之前定义 #include <linux/printk.h> ``` #### 2. 全局调试编译(可选) 在 Makefile 中添加编译标志: ```makefile EXTRA_CFLAGS += -DDEBUG # 开启该文件所有调试 # 或 EXTRA_CFLAGS += -DVERBOSE_DEBUG # 某些驱动专用调试宏 ``` --- ### 三、运行时调试控制 #### 1. 挂载 debugfs ```bash adb shell su mount -t debugfs none /sys/kernel/debug ``` #### 2. 动态启用调试输出 ```bash # 激活特定文件的调试 echo 'file xxx.c +p' > /sys/kernel/debug/dynamic_debug/control # 激活整个模块的调试 echo 'module xyz +p' > /sys/kernel/debug/dynamic_debug/control # 激活所有调试(谨慎使用) echo 'func pr_debug +p' > /sys/kernel/debug/dynamic_debug/control ``` #### 3. 查看当前调试状态 ```bash cat /sys/kernel/debug/dynamic_debug/control | grep enabled ``` --- ### 四、日志系统配置 #### 1. 调整 printk 日志级别 ```bash # 设置控制台输出最低级别为 DEBUG (7) echo 8 > /proc/sys/kernel/printk # 7 | 4 | 1 | 7 格式 ``` #### 2. 查看完整调试输出 ```bash adb shell dmesg -wH | grep -i "你的调试标记" ``` --- ### 五、Android 特殊注意事项 #### 1. 内核版本差异处理 | 内核版本 | 特性差异 | |---------|---------| | < 4.9 | 需要手动启用每个调试点 | | ≥ 4.19 | 支持通配符批量激活 | #### 2. 权限问题解决 ```bash # 针对 eng 版本系统 adb root adb remount # userdebug 版本可能需要: setenforce 0 ``` --- ### 六、调试验证示例 ```c // 内核代码示例 void critical_function(void) { pr_debug("CPU[%d] 进入关键路径", smp_processor_id()); // ... 业务逻辑 } ``` 执行验证命令: ```bash echo 'file driver/xyz/core.c +p' > /sys/kernel/debug/dynamic_debug/control adb shell dmesg -c > /dev/null # 清空日志 # 触发相关功能 adb shell dmesg | grep "进入关键路径" ``` --- ### 七、常见问题排查 1. **无输出情况处理** - 检查 `/proc/cmdline` 是否包含 `debug` 参数 - 确认驱动未使用 `pr_devel` 替代 `pr_debug` - 验证编译时是否优化掉了调试代码(检查反汇编) 2. **性能影响警告** ```bash # 高频调试语句建议添加频率限制 if (printk_ratelimit()) pr_debug("高频事件: count=%d\n", ++count); ``` --- 通过以上步骤,可以精确控制 `pr_debug` 的输出范围和详细程度。建议在开发阶段使用模块化调试策略,生产环境中及时关闭调试功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值