• Linux驱动开发(外传)---驱动开发调试方法


    前文回顾

    《Linux驱动开发(一)—环境搭建与hello world》
    《Linux驱动开发(二)—驱动与设备的分离设计》
    《Linux驱动开发(三)—设备树》
    《Linux驱动开发(四)—树莓派内核编译》
    《Linux驱动开发(五)—树莓派设备树配合驱动开发》
    《Linux驱动开发(六)—树莓派配合硬件进行字符驱动开发》
    《Linux驱动开发(七)—树莓派按键驱动开发》
    《Linux驱动开发(八)—树莓派SR04驱动开发》
    《Linux驱动开发(九)—树莓派I2C设备驱动开发(BME280)》
    《Linux驱动开发(十)—树莓派输入子系统学习(红外接收)》
    《Linux驱动开发(十一)—树莓派SPI驱动学习(OLED)》
    《Linux驱动开发(十二)—树莓派framebuffer学习(改造OLED)》
    《Linux驱动开发(十三)—USB驱动HID开发学习(鼠标)》
    《Linux驱动开发(十四)—USB驱动开发学习(键盘+鼠标)》
    《Linux驱动开发(十五)—如何使用内核现有驱动(显示屏)》
    《Linux驱动开发(十六)—块设备驱动》
    《Linux驱动开发(十七)—树莓派PWM驱动》
    《Linux驱动开发(十八)—网络(网卡)驱动学习》

    这篇学习一下内核开发或者是驱动开发的一些调试方法,算是系列内容的番外篇吧。
    在这里插入图片描述

    printk

    用的最多,最出名的调试手段。
    举个例子

    printk(KERN_CRIT  “Hello, world!\n”); 
    
    • 1

    printk可以指定一个LOG等级。

    #define KERN_EMERG	KERN_SOH "0"	/* system is unusable */
    #define KERN_ALERT	KERN_SOH "1"	/* action must be taken immediately */
    #define KERN_CRIT	KERN_SOH "2"	/* critical conditions */
    #define KERN_ERR	KERN_SOH "3"	/* error conditions */
    #define KERN_WARNING	KERN_SOH "4"	/* warning conditions */
    #define KERN_NOTICE	KERN_SOH "5"	/* normal but significant condition */
    #define KERN_INFO	KERN_SOH "6"	/* informational */
    #define KERN_DEBUG	KERN_SOH "7"	/* debug-level messages */
    
    #define KERN_DEFAULT	""		/* the default kernel loglevel */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    内核根据这个等级来判断是否在终端上打印消息。内核把比指定等级高的所有消息显示在终端。这个指定的等级可以在这里看到

    root@raspberrypi:/home/pgg/work/driver# cat /proc/sys/kernel/printk             
    3       4       1       3
    
    • 1
    • 2

    第一个3就是当前的指定等级,当小于(等级高于)3的时候,会直接在终端输出;
    第二个4表示,当没有指定log等级时候的默认等级;
    第三个1表示最高等级是1;
    第四个3表示默认控制台的log等级为3
    在这里插入图片描述

    如果我们要关闭某个模块的打印,可以通过修改全局的方式,但是这样就会关闭相同级别的打印,所以还是在模块内部通过宏定义进行打印的控制更便捷。
    下面是一个例子。

    #include    
    #include  
    #include  
    
    #define XXX_DEBUG "XXX:Line:%d "
    #undef PDEBUG             /* undef it, just in case */
    #ifdef XXX_DEBUG
        #ifdef __KERNEL__
    		  /* This one if debugging is on, and kernel space */
            #define PDEBUG(fmt, args...) printk( KERN_EMERG XXX_DEBUG fmt,__LINE__, ## args)
        #else
    		  /* This one for user space */
            #define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
        #endif
    #else
        #define PDEBUG(fmt, args...) /* not debugging: nothing */
    #endif
    #undef PDEBUGG
    #define PDEBUGG(fmt, args...) /* nothing: it’s a placeholder */
    	 
    
      
    static int __init hello_init(void)
    {
        PDEBUG("hello world\n");
        return 0;
    }
     
    static void __exit hello_exit(void)
    {
        PDEBUG("byebye world\n");
    }
    module_init(hello_init);
    module_exit(hello_exit);
    
    MODULE_LICENSE("GPL");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在加载模块和卸载模块的时候,会有打印输出

    root@raspberrypi:/home/pgg/work/driver# insmod kernel_debug.ko 
    root@raspberrypi:/home/pgg/work/driver# 
    Message from syslogd@raspberrypi at Aug 15 03:07:54 ...
     kernel:[ 2735.541133] XXX:Line:25 hello world
    
    root@raspberrypi:/home/pgg/work/driver# rmmod kernel_debug
    
    Message from syslogd@raspberrypi at Aug 15 03:08:02 ...
     kernel:[ 2743.211753] XXX:Line:31 byebye world
    root@raspberrypi:/home/pgg/work/driver# 
    root@raspberrypi:/home/pgg/work/driver# dmesg 
    [ 2735.541133] XXX:Line:25 hello world
    [ 2743.211753] XXX:Line:31 byebye world
    root@raspberrypi:/home/pgg/work/driver# 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    oops

    中文可以译为——WC。

    在linux中,oops表示“惊讶”,是一种信息提示,意味着系统上运行的某些东西违反了内核规定的规则;oops会生成一个崩溃签名“crash signature”,可以帮助内核开发人员找出错误并提高代码质量。
    其实这不是一个主动调用的方法,而是一个被动触发的系统技能,也许代码尝试采取不允许的代码路径或使用无效指针。不管它是什么,内核 —— 总是在监测进程的错误行为 —— 很可能会阻止特定进程,并将它做了什么的消息写入控制台、 /var/log/dmesg 或 /var/log/kern.log 中。
    在这里插入图片描述

    所以可以通过一个错误例子,来触发一下这个

    static int __init hello_init(void)
    {
    	*(int *)0 = 0;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    通过dmesg查看一下oops消息。

    [ 5228.069985] 8<--- cut here ---
    [ 5228.070030] Unable to handle kernel NULL pointer dereference at virtual address 00000000
    [ 5228.070055] pgd = 9ac88ec0
    [ 5228.070078] [00000000] *pgd=1927b835, *pte=00000000, *ppte=00000000
    [ 5228.070135] Internal error: Oops: 817 [#1] SMP ARM
    [ 5228.070152] Modules linked in: kernel_debug(+) rfcomm cmac algif_hash aes_arm_bs crypto_simd cryptd algif_skcipher af_alg bnep hci_uart btbcm bluetooth ecdh_generic ecc 8021q garp stp llc bmp280_i2c bmp280 industrialio regmap_i2c snd_soc_hdmi_codec brcmfmac vc4 cec brcmutil drm_kms_helper sha256_generic snd_soc_core cfg80211 snd_compress snd_pcm_dmaengine raspberrypi_hwmon rfkill syscopyarea sysfillrect sysimgblt fb_sys_fops i2c_bcm2835 pwm_bcm2835 snd_bcm2835(C) snd_pcm spi_bcm2835 snd_timer bcm2835_isp(C) bcm2835_codec(C) bcm2835_v4l2(C) v4l2_mem2mem snd bcm2835_mmal_vchiq(C) videobuf2_dma_contig videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 vc_sm_cma(C) videobuf2_common videodev mc fixed uio_pdrv_genirq uio i2c_dev drm drm_panel_orientation_quirks fuse backlight ip_tables x_tables ipv6 [last unloaded: kernel_debug]
    [ 5228.070615] CPU: 1 PID: 1524 Comm: insmod Tainted: G         C        5.15.55-v7+ #5
    [ 5228.070635] Hardware name: BCM2835
    [ 5228.070647] PC is at hello_init+0x18/0x1000 [kernel_debug]
    [ 5228.070678] LR is at do_one_initcall+0x50/0x250
    [ 5228.070704] pc : [<7f08d018>]    lr : [<80102368>]    psr: 60000013
    [ 5228.070718] sp : 992f3d90  ip : 992f3da0  fp : 992f3d9c
    [ 5228.070732] r10: 8f8bf968  r9 : 801ccb14  r8 : 8f8bf940
    [ 5228.070745] r7 : 00000000  r6 : 7f08d000  r5 : 80f05008  r4 : 7f321000
    [ 5228.070759] r3 : 9f54d47a  r2 : 9f54d47a  r1 : b775bbb4  r0 : 00000000
    [ 5228.070774] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
    [ 5228.070794] Control: 10c5383d  Table: 1907c06a  DAC: 00000055
    [ 5228.070806] Register r0 information: NULL pointer
    [ 5228.070825] Register r1 information: non-slab/vmalloc memory
    [ 5228.070844] Register r2 information: 0-page vmalloc region starting at 0x9ec00000 allocated at iotable_init+0x0/0x100
    [ 5228.070876] Register r3 information: 0-page vmalloc region starting at 0x9ec00000 allocated at iotable_init+0x0/0x100
    [ 5228.070904] Register r4 information: 4-page vmalloc region starting at 0x7f31f000 allocated at load_module+0x934/0x288c
    [ 5228.070937] Register r5 information: non-slab/vmalloc memory
    [ 5228.070955] Register r6 information: 2-page vmalloc region starting at 0x7f08d000 allocated at load_module+0x2618/0x288c
    [ 5228.070984] Register r7 information: NULL pointer
    [ 5228.071001] Register r8 information: slab kmalloc-64 start 8f8bf940 pointer offset 0 size 64
    [ 5228.071045] Register r9 information: non-slab/vmalloc memory
    [ 5228.071063] Register r10 information: slab kmalloc-64 start 8f8bf940 pointer offset 40 size 64
    [ 5228.071103] Register r11 information: non-slab/vmalloc memory
    [ 5228.071120] Register r12 information: non-slab/vmalloc memory
    [ 5228.071137] Process insmod (pid: 1524, stack limit = 0xe1a75653)
    [ 5228.071154] Stack: (0x992f3d90 to 0x992f4000)
    [ 5228.071173] 3d80:                                     992f3e14 992f3da0 80102368 7f08d00c
    [ 5228.071194] 3da0: 00000001 80a47df0 80f05830 00000000 992f3dd4 992f3dc0 80a47df0 8019fa64
    [ 5228.071215] 3dc0: 80f05830 80335be8 992f3e14 992f3dd8 80335be8 802f34a4 00000000 00000000
    [ 5228.071235] 3de0: 00000008 80a40f94 992f3f30 9f54d47a 992f3f30 7f321000 992f3f30 8f8bf180
    [ 5228.071256] 3e00: 00000001 8f8bf940 992f3e3c 992f3e18 80a40fb4 80102324 992f3e3c 992f3e28
    [ 5228.071277] 3e20: 80317a18 7f321000 992f3f30 00000001 992f3f14 992f3e40 801cffc8 80a40f6c
    [ 5228.071298] 3e40: 7f32100c 00007fff 7f321000 801cd1bc 80d14718 80d14730 80d146c0 80d146b4
    [ 5228.071319] 3e60: 00000000 7f08e444 7f321114 7f321214 80d14780 7f322000 7f321048 80f05008
    [ 5228.071339] 3e80: 80d1429c 00000001 00000000 80dc6260 80daf464 00000000 00000000 00000000
    [ 5228.071359] 3ea0: 00000000 00000000 6e72656b 00006c65 00000000 00000000 00000000 00000000
    [ 5228.071378] 3ec0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    [ 5228.071399] 3ee0: 00000000 9f54d47a 992f3f2c 80f05008 00000000 0002de04 00000003 80100244
    [ 5228.071421] 3f00: 992f2000 0000017b 992f3fa4 992f3f18 801d0d70 801ce270 992f3f2c 7fffffff
    [ 5228.071441] 3f20: 00000000 00000002 992f3f24 b88e0000 b88e00de b88e0240 b88e0000 00000e2c
    [ 5228.071462] 3f40: b88e0a44 b88e095c b88e07f8 00003000 000030c0 0000141c 0000310d 00000000
    [ 5228.071482] 3f60: 00000000 00000000 0000140c 00000016 00000017 0000000e 0000000c 00000007
    [ 5228.071503] 3f80: 00000000 9f54d47a 00000000 00000002 0b35ed00 0000017b 00000000 992f3fa8
    [ 5228.071523] 3fa0: 80100040 801d0cb4 00000000 00000002 00000003 0002de04 00000000 76fcc074
    [ 5228.071544] 3fc0: 00000000 00000002 0b35ed00 0000017b 00e32298 00000002 7eda9794 00000000
    [ 5228.071565] 3fe0: 7eda95c0 7eda95b0 00023bc0 76cb29e0 60000010 00000003 00000000 00000000
    [ 5228.071578] Backtrace: 
    [ 5228.071593] [<7f08d000>] (hello_init [kernel_debug]) from [<80102368>] (do_one_initcall+0x50/0x250)
    [ 5228.071631] [<80102318>] (do_one_initcall) from [<80a40fb4>] (do_init_module+0x54/0x218)
    [ 5228.071670]  r8:8f8bf940 r7:00000001 r6:8f8bf180 r5:992f3f30 r4:7f321000
    [ 5228.071682] [<80a40f60>] (do_init_module) from [<801cffc8>] (load_module+0x1d64/0x288c)
    [ 5228.071716]  r6:00000001 r5:992f3f30 r4:7f321000
    [ 5228.071727] [<801ce264>] (load_module) from [<801d0d70>] (sys_finit_module+0xc8/0xfc)
    [ 5228.071765]  r10:0000017b r9:992f2000 r8:80100244 r7:00000003 r6:0002de04 r5:00000000
    [ 5228.071779]  r4:80f05008
    [ 5228.071791] [<801d0ca8>] (sys_finit_module) from [<80100040>] (ret_fast_syscall+0x0/0x1c)
    [ 5228.071820] Exception stack(0x992f3fa8 to 0x992f3ff0)
    [ 5228.071839] 3fa0:                   00000000 00000002 00000003 0002de04 00000000 76fcc074
    [ 5228.071860] 3fc0: 00000000 00000002 0b35ed00 0000017b 00e32298 00000002 7eda9794 00000000
    [ 5228.071877] 3fe0: 7eda95c0 7eda95b0 00023bc0 76cb29e0
    [ 5228.071895]  r7:0000017b r6:0b35ed00 r5:00000002 r4:00000000
    [ 5228.071914] Code: e24cb004 e52de004 e8bd4000 e3a00000 (e5800000) 
    [ 5228.071932] ---[ end trace 1e2305052fc95bd2 ]---
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    出错原因,空指针
    在这里插入图片描述
    堆栈信息,查看调用关系
    在这里插入图片描述
    其他的部分涉及到了寄存器,类似汇编的地方,还需要深入学习一下。
    在这里插入图片描述

    dmesg

    前面已经用到了这个命令,这个就是查看内核的输出缓存记录,里面包含了从开机启动到当前,内核中输出的信息。
    简单列举几个常用方法

    参考《dmesg七种用法》

    分页显示

    我们可以使用如‘more’,‘tail, ‘less ’或者‘grep’文字处理工具来处理‘dmesg’命令的输出。
    由于dmesg日志的输出不适合在一页中完全显示,因此我们使用管道(pipe)将其输出送到more或者less命令单页显示。

    [root@tecmint.com ~]# dmesg | more
    [root@tecmint.com ~]# dmesg | less
    
    • 1
    • 2

    详细用法可以参考《Shell命令-文件及内容处理之more、less》

    前后几行

    只选择显示前面或者后面几行

    [root@tecmint.com ~]# dmesg | head  -20
    [root@tecmint.com ~]# dmesg | tail -20
    
    • 1
    • 2

    或者清空了数据,重新测试

    [root@tecmint.com ~]# dmesg -c
    
    • 1

    在这里插入图片描述

    选择等级显示

    dmesg --level=err,warn
    
    • 1

    然后系统的error以及程序员都不看的warning就展示了出来:
    level选项还可以填入别的等级,例如emerg、alert、crit、err、warn、notice、info、debug。

    实时监控dmesg日志输出

    在某些发行版中可以使用命令来实时监控dmesg的日志输出。

    tail -f /var/log/dmesg
    
    • 1

    也有的用

    tail -f /var/log/kern.log
    
    • 1

    还可以用

    [root@tecmint.com log]# watch "dmesg | tail -20"
    
    • 1

    在这里插入图片描述

    内核调试

    通过配置menuconfig,在hacking中,有很多调试选项,感兴趣的可以试试

    在这里插入图片描述
    在这里插入图片描述

    魔键

    如果一台服务器,连屏幕没反应,ssh远程连接不上,还有什么办法?这里还有一个魔键。参考资料
    《【调试工具】【sysrq】Sysrq魔术键介绍》
    在这里插入图片描述

    驱动相关

    前面的其实都是内核调试通用的做法,这里将一下驱动相关的调试。首先就是利用printk在各个入口函数,例如加载函数,probe函数,open 函数这类关键函数,增加调试打印。

    dts问题

    Status

    在编写或者修改dts的时候,一定要注意status这个值,是不是没有添加,或者没有设置为okey。

    查看设备树

    通过下面两个命令,可以查看当前设备树注册的部分设备,例如我们自己定义的gpio设备

    ls /sys/firmware/devicetree/base
    
    • 1

    或者

    ls /proc/device-tree/
    
    • 1
    设备种类查看位置
    输入子系统设备/dev/input
    I2C从设备/sys/bus/i2c/devices/
    SPI从站/sys/bus/spi/devices/
    framebuffer/dev/fbx
    块设备/dev/
    PWM/sys/class/pwm/
    网络设备/sys/class/net/

    与内核进行信息交互

    参考前面的一篇博客《Linux小知识—内核与用户态通讯方式之procfs》,也可以实时输出内核的一些信息。
    不过这个是单向查看,如果需要双向数据传输,可以通过ioctl函数进行开发。

    在这里插入图片描述

    部分参考资料

    《linux驱动程序调试方法》

    结束语

    一个很温馨的小故事。
    在这里插入图片描述

  • 相关阅读:
    城乡供水智慧化运营,喜提一等奖!
    前端js调用后端API获取数据的三种方法(2022.7.25)
    22年建模经验交流
    (2022最新)Java毕业设计参考题目-题目新颖(值得收藏)
    Android Camera2获取摄像头的视场角(FOV)信息
    Python在列表中如何对多个参数进行修改
    工业通信网关泵站远程监控管理系统
    在职场上有多少人输在了不会用Python数据分析
    PostgreSQL 插件 CREATE EXTENSION 原理
    代码随想录算法训练营第一天 | 704. 二分查找 | 27. 移除元素
  • 原文地址:https://blog.csdn.net/baidu_19348579/article/details/126340747