dev_info、dev_dbg、dev_err及動態調試

目前在kernel驅動代碼中,都不再建議直接使用printk直接添加打印信息,而是使用dev_infodev_dbgdev_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()信息
    • 控制某個文件所有dev_dbg(),echo -n "file xxx.c +p" > /mnt/dbg/dynamic_debug/control
    • 控制某個函數所有dev_dbg(),echo -n "func xxx +p" > /mnt/dbg/dynamic_debug/control
  • 運行程序,使用dmesg則可以看到相應dev_dbg()的輸出信息
  • 當調試結束,不再想輸出dev_dbg()信息了,使用下面命令關閉即可
    • echo -n "file xxx.c -p" > /mnt/dbg/dynamic_debug/control
    • 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_DEBUGCONFIG_DEBUG_FS,配合dbgfs動態觀測和調試內核代碼;
  • 正式版本,關閉CONFIG_DYNAMIC_DEBUGCONFIG_DEBUG_FS,所有dbgfs以及dev_dbg信息都從編譯階段自動移除;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章