前文回顾

《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”);

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 */

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

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

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

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

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>  #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");

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

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 worldroot@raspberrypi:/home/pgg/work/driver# rmmod kernel_debugMessage 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#

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;
}

通过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 ]---

出错原因,空指针

堆栈信息,查看调用关系

其他的部分涉及到了寄存器,类似汇编的地方,还需要深入学习一下。

dmesg

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

参考《dmesg七种用法》

分页显示

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

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

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

前后几行

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

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

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

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

选择等级显示

dmesg --level=err,warn

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

实时监控dmesg日志输出

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

tail -f /var/log/dmesg

也有的用

tail -f /var/log/kern.log

还可以用

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

内核调试

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


魔键

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

驱动相关

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

dts问题

Status

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

查看设备树

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

ls /sys/firmware/devicetree/base

或者

ls /proc/device-tree/
设备种类 查看位置
输入子系统设备 /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驱动程序调试方法》

结束语

一个很温馨的小故事。

Linux驱动开发(外传)---驱动开发调试方法相关推荐

  1. TRINAMIC静音步进驱动TMC2208模块的基本调试方法

    TRINAMIC静音步进驱动TMC2208模块的基本调试方法 TMC2208-LA Module小模块引脚说明 引脚图片 引脚对应关系以及说明 基本调试方法 TMC2208的有效电流(均方根电流)为1 ...

  2. linux系统c 如何使用教程,基于Linux操作系统的C语言编译和调试方法解析

    摘 要:文章先介绍了GCC编译器相关内容,包括GCC编译程序和GCC编译选项,随后介绍了GDB调试程序相关内容,包括GDB具体操作和GDB基础命令,最后介绍了C语言编写中的注意事项,希望能给相关人士提 ...

  3. Linux平台Segmentation fault(段错误)调试方法

    1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况. 2. 段错误的原因 段错 ...

  4. Linux环境下段错误分析及调试方法

    年轻时的每一个段错误,都会成为你程序人生上的垫脚石.如果是还在学习阶段的同学,希望能先通过自己的判断来找出段错误的地方. 本篇文章系转载及整理,原文链接如下: http://www.cnblogs.c ...

  5. 黑马程序员Linux系统开发视频之gdb调试方法

    一.gdb -- 命令     1.前提条件:可执行文件必须包含调试信息 gcc -gcc     2.gdb 文件名 -- 启动gdb调试     3.查看代码的命令         当前文件:   ...

  6. 在实际项目开发中keil的调试方法

    转载2015-06-14 20:23:04 一.在keilc的调试状态下,如何观察各个片内外设的运行状态?如何修改它们的设置?​ 在调试状态下,点击Peripherals菜单下的不同外设选项命令,就会 ...

  7. Linux C/C++程序崩溃bug调试方法

    C,C++程序最常见的崩溃问题就是内存问题,内存越界,访问空指针,野指针等都会造成程序崩溃.Linux系统中当程序运行过程中出现非法操作,系统会先发送对应的错误信号,每种错误信号都有默认的处理方式,比 ...

  8. Linux系统怎么打开pdb格式,linux环境下python的pdb调试方法

    Svn Patch 中文乱码 关于Patch svn打patch的介绍:巧用svn create patch(打补丁)方案解决定制版需求 svn创建patch 1.在SVN的提交列表中,右键选择&qu ...

  9. VxWorks操作系统shell命令与调试方法总结

    原文:http://blog.csdn.net/mao0514/article/details/38925581?utm_source=tuicool VxWorks下的调试手段 主要介绍在Torna ...

  10. 运行php程序cpu 100%,php 应用 cpu 100% 调试方法

    找出进程占用cpu高的原因. 进程占用cpu高,一般是由于进程长时间占用cpu,又没有主动释放占用.如果想主动释放cpu,可以调用sleep.在写程序的时候,尤其要注意while 等循环的地方. 找出 ...

最新文章

  1. app.listen(3000)与app是不一样的
  2. SQL SERVER使用ODBC 驱动建立的链接服务器调用存储过程时参数不能为NULL值
  3. html中视频变圆角,圆形视频和圆角视频的一种实现方式
  4. Mac平台上OpenCV开发环境搭建
  5. uniDBGrid导入数据库(转红鱼儿)
  6. 如何在32位程序中突破地址空间限制使用超过4G的内存
  7. token 微信access 过期_如何设计 QQ、微信等第三方账号登陆 ?以及设计数据库表!...
  8. 告别IE给我们的web开发带来的困扰(使用chrome frame v8引擎)
  9. hdu 1159(DP+字符串最长公共序列)
  10. python能做什么毕业设计-毕业设计涉及到python?看我用Python优雅的写论文!
  11. 微信小程序开发公司哪家好?
  12. bzoj1010 [HNOI2008]玩具装箱toy
  13. json序列化 java对象_Java中将JSON反序列化为泛型对象
  14. 判断访问浏览器的类型
  15. 立体合唱声效果器:Roland Cloud JUNO-60 Mac
  16. SuperMap系列——GIS数据之地图瓦片
  17. 计算机术语IP,什么是ip?网络ip和网络用语IP的含义!
  18. HelloWorld Detail Earth 3D Engine(一)总体介绍
  19. AI路径查找器的使用
  20. Ecshop及大商创198版本,解决Deprecated: preg_replace()报错

热门文章

  1. 中国氯化聚氯乙烯市场调研及投资策略分析报告2022-2028年
  2. 微PE安装系统 不显示U盘中镜像文件 的解决方法
  3. 关于孔明先生职称申请报告的回函
  4. ImageIO类的使用
  5. 不撞南墙不回头----深度优先搜索
  6. MongoDB for Java Programmer ——2
  7. 为何中国移动的上网速度最慢,无阻它成为最大运营商?
  8. java wms erp自动化立体仓库管理系统 进出库 源码 源代码 程序
  9. ubuntu18.04重装后的安装工作
  10. 英文文章单词自动查找脚本