Linux驱动调试中的Debugfs的使用简介
Linux驅動調試中的Debugfs的使用簡介?(2012-03-31 14:14)
在調試linux驅動的時候,可以用debugfs來調試,debugfs類似字符設備驅動一樣,甚至更簡單,不需要主設備號次設備號等等,只需要實現一個file_operations,然后通過debugfs_create_file就可以在debugfs中建立一個文件結點,就像字符設備驅動那樣,只需要對這個文件結點進行open就可以進行read、write、ioctl,等等操作,這些操作對應到我們在驅動里為debugfs準備的file_operations。
?
讓內核支持DEBUGFS,使能宏CONFIG_DEBUG_FS,在內核配置中選中,一般是在Kernel hacking中:
?
在實際的使用中,舉個例子來說明,在調試GPIO驅動的時候,我們可以通過debugfs來調試:
首先定義一個file_operations:
staticconst struct file_operations gpiolib_operations = {
.open = gpiolib_open,
.read = gpiolib_read,
.write = gpiolib_write,
.llseek = seq_lseek,
.release = single_release,
};
然后,建立一個debugfs文件結點:
(void)debugfs_create_file("gpio", S_IFREG | S_IRUGO,
NULL, NULL, &gpiolib_operations);
在實際的驅動中,建立debugfs文件結點一般在驅動初始化的時候。
根據我們的調試需要,實現讀寫操作,一般用得比較多的是read和write操作,所以在gpiolib_read和gpiolib_write里加入我們的調試代碼。調用GPIO驅動的時候,我的想法是,給GPIO結點發一個讀指令,那么就得傳入的gpio號的狀態,給GPIO結點發一個寫指令,那么就根據傳入的參數設置gpio的狀態。于是,我只需要實現write函數:
staticssize_t gpiolib_write(struct file *file, const char __user *buf,size_t size, loff_t *ppos) { charinfo[255]; int port=0,value=0; memset(info,0, 255); copy_from_user(info,buf, size); printk("gpio:%s\n",info); if((info[0]>= '0') && (info[0] <= '9')){port= (info[0] - 48)*10;if((info[1]>= '0') && (info[1] <= '9')){port+= (info[1] - 48);if(info[2]== ' '){if(info[3] == 'w'){value = (info[4] =='0')?0:1;} } } } if(info[3]== 'r'){gpio_direction_input(port);printk("gpio%dstatus = %d\n", port, __gpio_get_value(port)); }else if(info[3] == 'w'){printk("write%d to gpio%d\n", value, port);gpio_direction_output(port,value);__gpio_set_value(port,value);} return size; }這段代碼的意思,根據傳入的參數info作相應的操作,info的格式是:
info[0]和info[1]分別代表gpio號的十位和個位;
info[2]必須為空格;
info[3]為讀寫性質,‘w'為寫,'r'為讀;
info[4]如果為寫,那么它表示寫的狀態。
?
這樣就可以在驅動加載之后,用shell命令echo來進行調試了。
例如gpio號為57的端口控制蜂鳴器,gpio號為37的端口連接按鍵,那么:
蜂鳴器發聲:echo 57 w1 > gpio
蜂鳴器停止:echo 57 w0 > gpio
讀取按鍵狀態:echo 37 r > gpio
那么這個gpio文件結點在哪呢?
內核啟動后會把debugfs文件系統掛載到/sys/kernel/debug目錄下,我們的gpio文件結點就在這里。
如果沒有找到,那么可以手動掛載mount-t debugfs none /mnt,這樣就掛載到/mnt目錄下了。
?
PS:
更為強大的調試選項:
CONFIG_GPIO_SYSFS?? 定義此宏后 會在/sys/class/gpio/下面到處gpio的設備文件 可以通過此設備文件對gpio進行控制與讀取???
?
========================================================================================================================
Linux內核里的DebugFS
2011-01-17 23:52 by wwang, 4597 閱讀,?5?評論,?收藏,編輯
DebugFS,顧名思義,是一種用于內核調試的虛擬文件系統,內核開發者通過debugfs和用戶空間交換數據。類似的虛擬文件系統還有procfs和sysfs等,這幾種虛擬文件系統都并不實際存儲在硬盤上,而是Linux內核運行起來后才建立起來。
通常情況下,最常用的內核調試手段是printk。但printk并不是所有情況都好用,比如打印的數據可能過多,我們真正關心的數據在大量的輸出里不是那么一目了然;或者我們在調試時可能需要修改某些內核變量,這種情況下printk就無能為力,而如果為了修改某個值重新編譯內核或者驅動又過于低效,此時就需要一個臨時的文件系統可以把我們需要關心的數據映射到用戶空間。在過去,procfs可以實現這個目的,到了2.6時代,新引入的sysfs也同樣可以實現,但不論是procfs或是sysfs,用它們來實現某些debug的需求,似乎偏離了它們創建的本意。比如procfs,其目的是反映進程的狀態信息;而sysfs主要用于Linux設備模型。不論是procfs或是sysfs的接口應該保持相對穩定,因為用戶態程序很可能會依賴它們。當然,如果我們只是臨時借用procfs或者sysfs來作debug之用,在代碼發布之前將相關調試代碼刪除也無不可。但如果相關的調試借口要在相當長的一段時間內存在于內核之中,就不太適合放在procfs和sysfs里了。故此,debugfs應運而生。
默認情況下,debugfs會被掛載在目錄/sys/kernel/debug之下,如果您的發行版里沒有自動掛載,可以用如下命令手動完成:
?
| # mount -t debugfs none /your/debugfs/dir |
Linux內核為debugfs提供了非常簡潔的API,本文接下來將以一個實作為例來介紹,sample code可以從這里下載。
這個實作會在debugfs中建立如下的目錄結構:
其中,a對應模塊中的一個u8類型的變量,b和subdir下面的c都是對應模塊里的一個字符數組,只是它們的實現方式不同。
在module_init里,我們首先要建立根目錄mydebug:
my_debugfs_root = debugfs_create_dir("mydebug", NULL);
第一個參數是目錄的名稱,第二個參數用來指定這個目錄的上級目錄,如果是NULL,則表示是放在debugfs的根目錄里。
子目錄也是用debugfs_create_dir來實現:
sub_dir = debugfs_create_dir("subdir", my_debugfs_root);
建立文件a的代碼非常簡單:
debugfs_create_u8("a", 0644, my_debugfs_root, &a);
這表示文件名為“a”,文件屬性是0644,父目錄是上面建立的“mydebug”,對應的變量是模塊中的a。
Linux內核還提供了其他一些創建debugfs文件的API,請參考本文的附錄。
b是一個32-bytes的字符數組,在debugfs里,數組可以用blob wrapper來實現。
char hello[32] = "Hello world!\n";
structdebugfs_blob_wrapper b;
b.data = (void*)hello;
b.size = strlen(hello) + 1;
debugfs_create_blob("b", 0644, my_debugfs_root, &b);
這里需要注意的是,blob wrapper定義的數據只能是只讀的。在本例中,雖然我們把文件b的權限設定為0644,但實際這個文件還是只讀的,如果試圖改寫這個文件,系統將提示出錯。
如果需要對內核數組進行寫的動作,blob wrapper就無法滿足要求,我們只能通過自己定義文件操作來實現。在這個實作里,可以參考文件c的實現。c和b在模塊里對應著同一塊字符數組,不同的是,b是只讀的,而c通過自定義的文件操作同時實現了讀和寫。
staticint c_open(structinode *inode, structfile *filp) { filp->private_data = inode->i_private;return0; } staticssize_t c_read(structfile *filp, char__user *buffer, size_tcount, loff_t *ppos) { if(*ppos >= 32) return0; if(*ppos + count > 32) count = 32 - *ppos;if(copy_to_user(buffer, hello + *ppos, count)) return-EFAULT; *ppos += count;returncount; } staticssize_t c_write(structfile *filp, constchar __user *buffer,size_tcount, loff_t *ppos) { if(*ppos >= 32) return0; if(*ppos + count > 32) count = 32 - *ppos;if(copy_from_user(hello + *ppos, buffer, count)) return-EFAULT; *ppos += count;returncount; } structfile_operations c_fops = { .owner = THIS_MODULE,.open = c_open,.read = c_read,.write = c_write, }; debugfs_create_file("c", 0644, sub_dir, NULL, &c_fops);注:代碼里,c_open其實并沒有任何用處,因為c_read和c_write直接引用了全局變量hello。這里,我們也可以換一種寫法,在read/write函數里用filp->private_data來引用字符數組hello。
到這里,三個文件和子目錄已經創建完畢。在module_exit中,我們要記得釋放創建的數據。
debugfs_remove_recursive(my_debugfs_root);
debugfs_remove_recursive可以幫我們逐步移除每個分配的dentry,如果您想一個一個手動的移除,也可以直接調用debugfs_remove。
?
附錄:
創建和撤銷目錄及文件
structdentry *debugfs_create_dir(constchar *name, structdentry *parent);structdentry *debugfs_create_file(constchar *name, mode_t mode,structdentry *parent, void*data,conststruct file_operations *fops);void debugfs_remove(struct dentry *dentry);void debugfs_remove_recursive(struct dentry *dentry);創建單值文件
structdentry *debugfs_create_u8(constchar *name, mode_t mode, structdentry *parent, u8 *value);structdentry *debugfs_create_u16(constchar *name, mode_t mode,structdentry *parent, u16 *value);structdentry *debugfs_create_u32(constchar *name, mode_t mode,structdentry *parent, u32 *value);structdentry *debugfs_create_u64(constchar *name, mode_t mode, structdentry *parent, u64 *value);structdentry *debugfs_create_x8(constchar *name, mode_t mode, structdentry *parent, u8 *value);structdentry *debugfs_create_x16(constchar *name, mode_t mode, structdentry *parent, u16 *value);structdentry *debugfs_create_x32(constchar *name, mode_t mode, structdentry *parent, u32 *value);structdentry *debugfs_create_size_t(constchar *name, mode_t mode, structdentry *parent, size_t*value);structdentry *debugfs_create_bool(constchar *name, mode_t mode, structdentry *parent, u32 *value);其中,后綴為x8、x16、x32的這三個函數是指debugfs中的數據用十六進制表示。
創建BLOB文件
structdebugfs_blob_wrapper {void*data;unsignedlong size;};structdentry *debugfs_create_blob(constchar *name, mode_t mode,structdentry *parent, structdebugfs_blob_wrapper *blob);其它
structdentry *debugfs_rename(structdentry *old_dir, structdentry *old_dentry,structdentry *new_dir, constchar *new_name);structdentry *debugfs_create_symlink(constchar *name,structdentry *parent, constchar *target);?
====================================================================================================================================
?
以前都習慣用 printk 和 /proc 做輸入輸出的動作, 不過 debugfs 看起來是 User space 和 kernel space 交流更好的選擇.
先確認 Enable Kernel debugfs Function
Kernel hacking —>
-*- Debug Filesystem
先來個簡單的範例,
在你要 debug 的 modules 內, 加入 debugfs 的 include file
#include <linux/debugfs.h>
要將想要輸出的變數, 假設叫 pcie0_linked 輸出到 debugfs 上, 在 initial code 的地方加上
debugfs_create_u32("pcie0_linked", 0644, NULL, &pcie0_linked);
接下來就可以重開機了 load 新 kernel 了,
mount debugfs
$ mount -t debugfs debug /debugfs
或是寫在 /etc/fstab
debugfs /debugfs debugfs debug
這時就可以 ls /debugfs/ , 就會出現 pcie0_linked 的檔案.
$ cat /debugfs/pcie0_linked1$ echo 0 > /debugfs/pcie0_linked$ cat /debugfs/pcie0_linked0像是 procfs 一樣, debugfs 也有 create directory 的 function, 以便讓變數可以在目錄內
我們小小改一下上面的程式, 加上 create_dir 的功能
改了以上的輸出, 接下來就可以在 /debugfs 下, 看到多了一個 pcie 的目錄, 而 pcie0_linked 就在裡面.
如果想用 hex(16 進位), 可以改用 debugfs_create_x32.
proc file system 最棒的就是可以讀寫檔案了, 可以做更多的控制.
debugfs 也有一個 function 可以讓使用者做檔案讀寫, 這邊寫一個簡單的 sample.
多 include 一個 header
#include <linux/seq_file.h>static int pcie_reg_open(struct seq_file *s, void *data) { seq_printf(s, "pcie0_link status : %s\n", pcie0_linked == 1 ? "Enable": "D return 0; }static int pcie_single_open(struct inode *inode, struct file *file) { return single_open(file, pcie_reg_open, inode->i_private); } static ssize_t pcie_debug_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { char buf[20];if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT;printk("%s: %s \n",__FUNCTION__, buf);return count; }static const struct file_operations pcie_ios_fops = { .open = pcie_single_open, .read = seq_read, .write = pcie_debug_write, .llseek = seq_lseek, .release = single_release,}; debugfs_create_file("file", 0644, pcie_dir, NULL, &pcie_ios_fops);這樣
$ cat /debugs/pcie/file 會顯示 pcie0_link status : Enable 而 $ echo "richliu" > /debugfs/pcie/file 會顯示 pcie_debug_write: richliu$?
最後要介紹的是比較特別的一種格式 blob, 這是可以傳 binary 到 user space 的格式, blob 的 struct 是
struct debugfs_blob_wrapper { void *data; unsigned long size; };
在剛剛的 Code 加上
static struct debugfs_blob_wrapper blob; –> 最好放 global.
char data[100];
sprintf(data, "Data Pointer is : %08X \n", data);
blob.data = data;
blob.size = 100;
debugfs_create_blob("blob", S_IRUSR, pcie_dir, &blob);
在 Linux 下直接用 hexdump 去讀資料出來
$ hexdump /debugfs/pcie/blob -c
0000000 D a t a P o i n t e r i s
0000010 : C 4 0 5 C 1 6 0 \n \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0000060
請記得 Blob 這個檔案是 Read Only, 只能傳出, 不能傳入…
參考:
Debugfs
Debugfs 中譯版(好像是從匪區抄過來的?)
總結
以上是生活随笔為你收集整理的Linux驱动调试中的Debugfs的使用简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zynq 文件系统中加载PL fpga.
- 下一篇: busybox编译笔记