目前在kernel驱动代码中,都不再建议直接使用printk直接添加打印信息,而是使用dev_info,dev_dbg,dev_err之类的函数代替,虽然这些dev_xxx函数的本质还是使用printk打印的,但是相比起printk:

  • 支持打印模块信息、dev信息

  • 支持动态调试(dynamic debug)方式

下面简述下这几个dev_xxx函数的基本使用规则,以及动态调试使用方式。

  • dev_info():启动过程、或者模块加载过程等“通知类的”信息等,一般只会通知一次,例如probe函数;

  • dev_dbg():一般使用在普通错误,如-EINVAL、-ENOMEM等errno发生处,用于调试;

  • dev_err():一般使用在严重错误,尤其是用户无法得到errno的地方,或者程序员不容易猜测系统哪里出了问题的地方;

动态调试使用方法

  • 打开内核动态调试开关,make menuconfig选中CONFIG_DYNAMIC_DEBUG以及CONFIG_DEBUG_FS

  • Linux启动后,使用命令行挂载上dbgfs

mkdir /mnt/dbg
mount -t debugfs none /mnt/dbg
  • 使用下面方式控制你想输出dev_dbg()信息

  • 1.控制某个文件所有dev_dbg(),echo -n "file xxx.c +p" > /mnt/dbg/dynamic_debug/control

  • 2.控制某个函数所有dev_dbg(),echo -n "func xxx +p" > /mnt/dbg/dynamic_debug/control

  • 运行程序,使用dmesg则可以看到相应dev_dbg()的输出信息

  • 当调试结束,不再想输出dev_dbg()信息了,使用下面命令关闭即可

  • 1.echo -n "file xxx.c -p" > /mnt/dbg/dynamic_debug/control

  • 2.echo -n "func xxx -p" > /mnt/dbg/dynamic_debug/control

例子

echo -n "file ca_dsc_core.c +p" > /mnt/dbg/dynamic_debug/control 则打印ca_dsc_core.c所有的dev_dbg()信息
echo -n "func ca_dsc_read +p" > /mnt/dbg/dynamic_debug/control 则打印ca_dsc_read()函数所有dev_dbg()信息

动态打印调试的基本原理

当编译选项CONFIG_DYNAMIC_DEBUG打开的时候,在编译阶段,kernel会把所有使用dev_dbg()的信息记录在一个table中,这些信息我们可以从/mnt/dbg/dynamic_debug/control解析出来:

# cat /mnt/dbg/dynamic_debug/control
... ...
... ...
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:800 [alidsc]ca_dsc_probe_dt =_ "get dev-index error12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:789 [alidsc]ca_dsc_probe_dt =_ "get clk error12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:292 [alidsc]ca_dsc_read =p "read: session#%d read returned %d bytes12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:234 [alidsc]ca_dsc_read =p "read: session#%d read request: %zd bytes12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:435 [alidsc]ca_dsc_vm_fault =_ "dsc_vm_fault: buffer#%d release %d bytes for session#%d12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:112 [alidsc]ca_dsc_open =_ "dsc_se: failed register se12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:755 [alidsc]ca_dsc_splice_write =_ "splice_write: session#%d dsc_from_pipe %d bytes12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:729 [alidsc]ca_dsc_splice_write =_ "splice_write: session#%d count %zd bytes12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:619 [alidsc]ca_dsc_splice_read =_ "splice_read: session#%d ret %zd bytes12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:532 [alidsc]ca_dsc_splice_read =_ "splice_read: session#%d request %zd bytes12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:871 [alidsc]ca_dsc_probe =_ "Get DSC handler error!12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:820 [alidsc]ca_dsc_probe =_ "Failed to parse DT12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:923 [alidsc]ca_dsc_remove =_ "get clk error12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:396 [alidsc]ca_dsc_write =_ "write: session#%d ret %zd12"
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:325 [alidsc]ca_dsc_write =_ "write: session#%d count %zd12"
... ...
... ...
net/ipv4/ping.c:965 [ping]ping_rcv =_ "no socket, dropping12"
net/ipv4/ping.c:960 [ping]ping_rcv =_ "rcv on socket %p12"
net/ipv4/ping.c:953 [ping]ping_rcv =_ "ping_rcv(skb=%p,id=%04x,seq=%04x)12"
net/ipv4/ping.c:932 [ping]ping_queue_rcv_skb =_ "ping_queue_rcv_skb -> failed12"
net/ipv4/ping.c:929 [ping]ping_queue_rcv_skb =_ "ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)12"
net/ipv4/ping.c:921 [ping]ping_recvmsg =_ "ping_recvmsg -> %d12"
net/ipv4/ping.c:840 [ping]ping_recvmsg =_ "ping_recvmsg(sk=%p,sk->num=%u)12"
net/ipv4/ping.c:693 [ping]ping_v4_sendmsg =_ "ping_v4_sendmsg(sk=%p,sk->num=%u)12"
net/ipv4/ping.c:197 [ping]ping_lookup =_ "found: %p: num=%d, daddr=%pI4, dif=%d12"
net/ipv4/ping.c:189 [ping]ping_lookup =_ "iterate12"
net/ipv4/ping.c:176 [ping]ping_lookup =_ "try to find: num = %d, daddr = %pI4, dif = %d12"
net/ipv4/ping.c:505 [ping]ping_err =_ "err on socket %p12"
net/ipv4/ping.c:502 [ping]ping_err =_ "no socket, dropping12"
net/ipv4/ping.c:498 [ping]ping_err =_ "ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)12"
net/ipv4/ping.c:304 [ping]ping_check_bind_addr =_ "ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)12"
net/ipv4/ping.c:445 [ping]ping_bind =_ "ping_v4_bind -> %d12"
net/ipv4/ping.c:423 [ping]ping_bind =_ "after bind(): num = %d, dif = %d12"
net/ipv4/ping.c:286 [ping]ping_close =_ "isk->refcnt = %d12"
net/ipv4/ping.c:285 [ping]ping_close =_ "ping_close(sk=%p,sk->num=%u)12"
net/ipv4/ping.c:153 [ping]ping_unhash =_ "ping_unhash(isk=%p,isk->num=%u)12"
net/ipv4/ping.c:146 [ping]ping_hash =_ "ping_hash(sk->port=%u)12"
net/ipv4/ping.c:67 [ping]ping_hashfn =_ "hash(%d) = %d12"
net/ipv4/ping.c:130 [ping]ping_get_port =_ "was not hashed12"
net/ipv4/ping.c:127 [ping]ping_get_port =_ "found port/ident = %d12"

以其中一条为例子:

drivers/alidrivers/modules/alidsc/ca_dsc_core.c:800 [alidsc]ca_dsc_probe_dt =_ "get dev-index error12" 则不会打印
drivers/alidrivers/modules/alidsc/ca_dsc_core.c:800 [alidsc]ca_dsc_probe_dt =p "get dev-index error12" 则会打印

所以在应用层,用户就可以通过使用echo来控制dynamic_debug/control文件,进而控制是否打印某个dev_dbg()信息!

dev_dbg()对于分析某些内核子系统或者驱动流程也十分有意义,例如,使能net/ipv4/ping.c的调试开关,则可以观测ping的运行原理。

代码分析

从代码角度,也很容易看出dev_dbg()的设计:

include/linux/device.h include/linux/dynamic_debug.h lib/dynamic_debug.c

//使能CONFIG_DYNAMIC_DEBUG后则根据control信息动态打印
#if defined(CONFIG_DYNAMIC_DEBUG)#define dev_dbg(dev, format, ...)       do {           dynamic_dev_dbg(dev, format, ##__VA_ARGS__); } while (0)
//使能DEBUG,则打印整个kernel的dev_dbg信息
#elif defined(DEBUG)#define dev_dbg(dev, format, arg...)  dev_printk(KERN_DEBUG, dev, format, ##arg)
//都不使能,dev_dbg不打印任何东西
#else#define dev_dbg(dev, format, arg...)    ({        if (0)       dev_printk(KERN_DEBUG, dev, format, ##arg); 0;       })
#endif

下面的dynamic_dev_dbg()实现显然可以看出,打印是根据descriptor的标志位_DPRINTK_FLAGS_PRINT进行打印的,而标志位是可以通过dbgfs进行控制的。

#define dynamic_dev_dbg(dev, fmt, ...)
do {        DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);  if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) __dynamic_dev_dbg(&descriptor, dev, fmt, ##__VA_ARGS__);
} while (0)

好处

  • 开发版本,打开CONFIG_DYNAMIC_DEBUG和CONFIG_DEBUG_FS,配合dbgfs动态观测和调试内核代码;

  • 正式版本,关闭CONFIG_DYNAMIC_DEBUG和CONFIG_DEBUG_FS,所有dbgfs以及dev_dbg信息都从编译阶段自动移除;

5T技术资源大放送!包括但不限于:C/C++,Arm, Linux,Android,人工智能,单片机,树莓派,等等。在公众号内回复「peter」,即可免费获取!!

 记得点击分享在看,给我

Linux内核中dev_info、dev_dbg、dev_err及动态调试相关推荐

  1. Linux中文件描述符1,linux内核中的文件描述符(一)--基础知识简介

    原标题:linux内核中的文件描述符(一)--基础知识简介 Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123(http://blo ...

  2. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  3. Linux内核中的同步原语:自旋锁,信号量,互斥锁,读写信号量,顺序锁

    Linux内核中的同步原语 自旋锁,信号量,互斥锁,读写信号量,顺序锁 rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 1. Linux 内核中的同步原 ...

  4. linux内核中锁有哪些,Linux内核中有哪些锁

    Linux内核中的各种锁 在LInux操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问.尤其是在多处理器系统上,更需 ...

  5. linux内核中的文件描述符(一)--基础知识简介

    linux内核中的文件描述符(一)--基础知识简介 Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123(http://blog.cs ...

  6. KSM(Kernel Samepage Merging) 剖析:Linux 内核中的内存去耦合

    简介:作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通过 ...

  7. 论文中文翻译——Double-Fetch情况如何演变为Double-Fetch漏洞:Linux内核中的双重获取研究

    本论文相关内容 论文下载地址--Web Of Science 论文中文翻译--How Double-Fetch Situations turn into Double-Fetch Vulnerabil ...

  8. Linux内核中的软中断、tasklet和工作队列详解

    本文基于Linux2.6.32内核版本. 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的"下半部"(bottom half)演 ...

  9. Linux内核中的软中断、tasklet和工作队列具体解释

    [TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...

  10. Linux内核中的内存管理(图例解析)

    一 ,内核管理内存的方式 (1)内核把物理页作为内存管理的基本单位,内存管理单元通常以页为单位进行处理,所以,从虚拟内存角度来看,页就是最小单位. 大多数32位系统支持4kb的页,64位系统支持8kb ...

最新文章

  1. Oracle 与SQL Server 2000常用函数对照 [摘抄]
  2. url-pattern配置
  3. 一步步学习SpringBoot(一) 快速搭建一个web
  4. SAAS,象B2C那样的B2B分销系统
  5. eigen冲突 sophus 安装_SLAM-Sophus模板库安装总结
  6. 560. 和为K的子数组
  7. python列表截取双冒号“::“用法
  8. Redis集群部署(半自动)
  9. 编程语言“鄙视链” +1?亚马逊力捧 Rust,Go 技术负责人连发 14 条推特抵制“拉踩”
  10. mysql5.7.10安装时密码_Windows10中MySQL5.7安装及修改root密码的详细方法
  11. Linux安装Oracle,卡在60%处的解决方法
  12. fireworks切图
  13. matplotlib绘制李萨如图(一) 静态2D李萨如图
  14. 华为WLAN基本概述
  15. 个人简历模板 个人简历表下载 个人简历模板下载
  16. 解决xcel数据导入MySQL数据库【日期数据格式混乱】的问题
  17. python-opencv下读取影像释放内存
  18. 常见图片文件格式简析
  19. cf1292 C. Xenon's Attack on the Gangs
  20. [小工具] 微信小程序代码压缩器

热门文章

  1. Android局域网工具,局域网内连接Android进行调试
  2. GMM-HMM 详解
  3. 使用和风天气接口获取天气信息
  4. 我的世界服务器的无限方块指令,我的世界用指令方块造无限木头 | 手游网游页游攻略大全...
  5. 手把手教你用Java获取IP归属地
  6. 网络电视广告屏蔽教程
  7. 安装工程造价课程设计_安装工程造价课程设计心得体会及建议
  8. tps在区块链是什么意思_超百万的TPS拯救不了区块链技术,区块链还差什么?
  9. 【MyBatis】框架-----MyBatis的基础知识以及案例分析
  10. pyhanlp 繁简转换,拼音转换与字符正则化