专栏首页人人都是极客Linux内核中dev_info、dev_dbg、dev_err及动态调试

Linux内核中dev_info、dev_dbg、dev_err及动态调试

目前在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信息都从编译阶段自动移除;

本文分享自微信公众号 - 人人都是极客(rrgeek),作者:土豆居士

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux的notifier机制的应用

    在linux内核系统中,各个模块、子系统之间是相互独立的。Linux内核可以通过通知链机制来获取由其它模块或子系统产生的它感兴趣的某些事件。

    233333
  • 高通Audio中ASOC的machine驱动

    ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的...

    233333
  • Linux音频驱动-AOSC之Platform

    在ASOC在Platform部分,主要是平台相关的DMA操作和音频管理。大概流程先将音频数据从内存通过DMA方式传输到CPU侧的dai接口,然后通过CPU的da...

    DragonKingZhu
  • Linux音频驱动-AOSC之Codec

    ASOC的出现是为了让Codec独立于CPU,减少和CPU之间的耦合,这样同一个Codec驱动无需修改就可以适用任何一款平台。还是以下图做参考例子:

    DragonKingZhu
  • I2C总线架构 之 I2C核心

    在之前的 「《I2C总线架构 之 设备驱动》」 和 「《I2C总线架构 之 总线驱动》」 中一再提到i2c核心,本篇文章就总结一下i2c核心的主要功能。

    开源519
  • 【调试笔记】韦东山:在100ASK_IMX6ULL板子上支持其他型号的屏幕

    很多学员有过STM32的学习经验,他们手上的开发板很多,LCD也很多。 一个LCD还挺贵的,不能浪费。 各家的LCD引脚顺序都不一样,所以别家的LCD不能直...

    韦东山
  • 【腾讯连连IoT开发大赛】移动物联网环境监测站

    本产品通过物联网功能实现可移动的环境监测站,随时随地监测环境数据的同时上传数据到云平台,方便监测人员和监督人员同步测量环境数据。因为使用了4G网络,所以相比于N...

    极速紫韵
  • NFC驱动调试

    1.NFC基本概念: NFC 又称为近场通信,是一种新兴技术,可以在彼此靠近的情况下进行数据交换,是由非接触式射频识别(RFID)及互连互通技术整合演变而来,通...

    233333
  • 30.Linux-RTC驱动分析及使用

     linux中的rtc驱动位于drivers/rtc下,里面包含了许多开发平台的RTC驱动,我们这里是以S3C24xx为主,所以它的RTC驱动为rtc-s3c....

    张诺谦

扫码关注云+社区

领取腾讯云代金券