在Linux driver開發的初期我們常會為了程式的開發方便,及除錯在裡面加了很多的debug message,
等到開發到了一定的階段才會拿掉,這邊分享一個比較方便的方法。
首先先來看一下程式碼
/* Debug macros*/
#if defined(DEBUG) /* limited debug messages */
#define GPSDRV_DBG(fmt, arg...) \
printk(KERN_INFO "[GPS] (gpsdrv):"fmt"\n" , ## arg)
#define GPSDRV_VER(fmt, arg...)
#elif defined(VERBOSE) /* very verbose */
#define GPSDRV_DBG(fmt, arg...) \
printk(KERN_INFO "[GPS] (gpsdrv):"fmt"\n" , ## arg)
#define GPSDRV_VER(fmt, arg...) \
printk(KERN_INFO "[GPS] (gpsdrv):"fmt"\n" , ## arg)
#define GPSDRV_ERR(fmt, arg...) \
printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
#else /* Error msgs only */
#define GPSDRV_ERR(fmt, arg...) \
printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
#define GPSDRV_VER(fmt, arg...)
#define GPSDRV_DBG(fmt, arg...)
#endif
說明:
在driver裡面我們自定義了3層級的debug msg (GPSDRV_DBG, GPSDRV_VER, GPSDRV_ERR)
然後可以依據我們的需求來指定debug msg 的 level,例如:
driver開發完成之後我們可以undef DEBUG 和 undef VERBOSE,
這樣的話只有在driver出現關鍵錯誤的時候,才會印出debug msg。
#else /* Error msgs only */
#define GPSDRV_ERR(fmt, arg...) \
printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
#define GPSDRV_VER(fmt, arg...)
#define GPSDRV_DBG(fmt, arg...)
我想看到這邊大家已經看懂原理了,再來我們看一下很像火星文的這一行。
#define GPSDRV_ERR(fmt, arg...) \
printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
這個是gcc的特殊語法,我們可以利用下面例子讓各位更容易了解。
標準C只支持可變參數的函數,意味著函數的參數是不固定的,例如printf()函數的原型為:
int printf( const char *format [, argument]... );
而在GNU C中,巨集(macro)也可以接受可變數目的參數,例如:
#define GPSDRV_ERR(fmt, arg...) \
printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
其中"%s:%d"會被對應到fmt,而, filename, line會被對應到arg...,
如何把arg 對應到多個變數呢?就是靠... 和## 來達成,
arg... 表示arg的參數可以是零個或多個,這些參數以及參數之間的逗號構成,
## 表示 ##arg之前的字串要保留
我們可以利用gcc -E 來觀看程式碼被展開之後的樣子,例如:
GPSDRV_ERR("%s:%d",filename,line)
會被展開為:
printk(KERN_ERR "[GPS] (gpsdrv):%s:%d\n", filename, line)