DebugFS

Copyright © 2009 Jonathan Corbet <corbet@lwn.net>

Debugfs 存在,是核心開發者向用戶空間提供資訊的一種簡單方式。 與 /proc(僅用於關於程序的資訊)或 sysfs(具有嚴格的每個檔案一個值的規則)不同,debugfs 沒有任何規則。 開發者可以將他們想要的任何資訊放在那裡。 debugfs 檔案系統也不打算用作使用者空間的穩定 ABI; 理論上,對匯出到那裡的檔案沒有穩定性約束。 然而,現實世界並不總是那麼簡單 [1]; 即使是 debugfs 介面,最好也以需要永久維護的思路來設計。

Debugfs 通常透過如下命令掛載

mount -t debugfs none /sys/kernel/debug

(或者等效的 /etc/fstab 行)。 預設情況下,debugfs 根目錄只能由 root 使用者訪問。 要更改對樹的訪問許可權,可以使用 “uid”、“gid” 和 “mode” 掛載選項。

請注意,debugfs API 僅以 GPL 方式匯出到模組。

使用 debugfs 的程式碼應包含 <linux/debugfs.h>。 然後,首先要做的是建立至少一個目錄來儲存一組 debugfs 檔案

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

如果呼叫成功,此呼叫將在指定的父目錄下建立一個名為 name 的目錄。 如果 parent 為 NULL,則將在 debugfs 根目錄中建立該目錄。 成功後,返回值是一個 struct dentry 指標,可用於在該目錄中建立檔案(並在最後清理它)。 ERR_PTR(-ERROR) 返回值表示出現問題。 如果返回 ERR_PTR(-ENODEV),則表明核心在構建時沒有 debugfs 支援,並且下面描述的任何函式都將不起作用。

在 debugfs 目錄中建立檔案的最通用方法是使用

struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                   struct dentry *parent, void *data,
                                   const struct file_operations *fops);

在這裡,name 是要建立的檔案的名稱,mode 描述了檔案應具有的訪問許可權,parent 指示應儲存該檔案的目錄,data 將儲存在生成的 inode 結構的 i_private 欄位中,而 fops 是一組實現檔案行為的檔案操作。 至少應提供 read() 和/或 write() 操作; 其他操作可以根據需要包括在內。 同樣,返回值將是指向已建立檔案的 dentry 指標,出錯時為 ERR_PTR(-ERROR),如果缺少 debugfs 支援,則為 ERR_PTR(-ENODEV)。

要建立一個具有初始大小的檔案,可以使用以下函式代替

void debugfs_create_file_size(const char *name, umode_t mode,
                              struct dentry *parent, void *data,
                              const struct file_operations *fops,
                              loff_t file_size);

file_size 是初始檔案大小。 其他引數與函式 debugfs_create_file 相同。

在許多情況下,實際上沒有必要建立一組檔案操作; debugfs 程式碼為簡單情況提供了許多輔助函式。 包含單個整數值的檔案可以使用以下任何一種方式建立

void debugfs_create_u8(const char *name, umode_t mode,
                       struct dentry *parent, u8 *value);
void debugfs_create_u16(const char *name, umode_t mode,
                        struct dentry *parent, u16 *value);
void debugfs_create_u32(const char *name, umode_t mode,
                        struct dentry *parent, u32 *value);
void debugfs_create_u64(const char *name, umode_t mode,
                        struct dentry *parent, u64 *value);

這些檔案支援讀取和寫入給定的值; 如果不應寫入特定檔案,只需相應地設定模式位即可。 這些檔案中的值採用十進位制; 如果十六進位制更合適,則可以使用以下函式代替

void debugfs_create_x8(const char *name, umode_t mode,
                       struct dentry *parent, u8 *value);
void debugfs_create_x16(const char *name, umode_t mode,
                        struct dentry *parent, u16 *value);
void debugfs_create_x32(const char *name, umode_t mode,
                        struct dentry *parent, u32 *value);
void debugfs_create_x64(const char *name, umode_t mode,
                        struct dentry *parent, u64 *value);

只要開發人員知道要匯出的值的大小,這些函式就很有用。 但是,某些型別在不同的體系結構上可能具有不同的寬度,從而使情況變得複雜。 有一些函式旨在幫助處理此類特殊情況

void debugfs_create_size_t(const char *name, umode_t mode,
                           struct dentry *parent, size_t *value);

可以預料的是,此函式將建立一個 debugfs 檔案來表示 size_t 型別的變數。

類似地,有用於 unsigned long 型別變數的輔助函式,採用十進位制和十六進位制

struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
                                    struct dentry *parent,
                                    unsigned long *value);
void debugfs_create_xul(const char *name, umode_t mode,
                        struct dentry *parent, unsigned long *value);

布林值可以透過以下方式放置在 debugfs 中

void debugfs_create_bool(const char *name, umode_t mode,
                         struct dentry *parent, bool *value);

讀取結果檔案將產生 Y(對於非零值)或 N,後跟一個換行符。 如果寫入該檔案,它將接受大寫或小寫的值,或 1 或 0。 任何其他輸入都將被靜默忽略。

此外,atomic_t 值可以透過以下方式放置在 debugfs 中

void debugfs_create_atomic_t(const char *name, umode_t mode,
                             struct dentry *parent, atomic_t *value)

讀取此檔案將獲取 atomic_t 值,寫入此檔案將設定 atomic_t 值。

另一個選項是匯出任意二進位制資料塊,使用此結構和函式

struct debugfs_blob_wrapper {
    void *data;
    unsigned long size;
};

struct dentry *debugfs_create_blob(const char *name, umode_t mode,
                                   struct dentry *parent,
                                   struct debugfs_blob_wrapper *blob);

讀取此檔案將返回 debugfs_blob_wrapper 結構指向的資料。 一些驅動程式使用 “blobs” 作為返回幾行(靜態)格式化文字輸出的簡單方法。 此函式可用於匯出二進位制資訊,但似乎沒有程式碼在主線中這樣做。 請注意,使用 debugfs_create_blob() 建立的所有檔案都是隻讀的。

如果要轉儲暫存器塊(在開發過程中經常發生,即使很少有此類程式碼到達主線),debugfs 提供了兩個函式:一個用於建立僅暫存器檔案,另一個用於將暫存器塊插入另一個順序檔案的中間

struct debugfs_reg32 {
    char *name;
    unsigned long offset;
};

struct debugfs_regset32 {
    const struct debugfs_reg32 *regs;
    int nregs;
    void __iomem *base;
    struct device *dev;     /* Optional device for Runtime PM */
};

debugfs_create_regset32(const char *name, umode_t mode,
                        struct dentry *parent,
                        struct debugfs_regset32 *regset);

void debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,
                     int nregs, void __iomem *base, char *prefix);

“base” 引數可以為 0,但您可能希望使用 __stringify 構建 reg32 陣列,並且許多暫存器名稱(宏)實際上是暫存器塊的基址上的位元組偏移量。

如果要將 u32 陣列轉儲到 debugfs 中,可以使用以下命令建立一個檔案

struct debugfs_u32_array {
    u32 *array;
    u32 n_elements;
};

void debugfs_create_u32_array(const char *name, umode_t mode,
                    struct dentry *parent,
                    struct debugfs_u32_array *array);

“array” 引數包裝指向陣列資料的指標及其元素的數量。 注意:一旦建立了陣列,就無法更改其大小。

有一個輔助函式可以建立與裝置相關的 seq_file

void debugfs_create_devm_seqfile(struct device *dev,
                             const char *name,
                             struct dentry *parent,
                             int (*read_fn)(struct seq_file *s,
                                     void *data));

“dev” 引數是與此 debugfs 檔案相關的裝置,“read_fn” 是一個函式指標,用於呼叫以列印 seq_file 內容。

還有幾個面向目錄的輔助函式

struct dentry *debugfs_change_name(struct dentry *dentry,
                                      const char *fmt, ...);

struct dentry *debugfs_create_symlink(const char *name,
                                      struct dentry *parent,
                                      const char *target);

呼叫 debugfs_change_name() 將為現有 debugfs 檔案提供一個新名稱,始終在同一個目錄中。 在呼叫之前,new_name 必須不存在; 成功時返回值為 0,失敗時返回 -E...。 可以使用 debugfs_create_symlink() 建立符號連結。

所有 debugfs 使用者都必須考慮一個重要事項:debugfs 中建立的任何目錄都不會自動清理。 如果一個模組在沒有顯式刪除 debugfs 條目的情況下解除安裝,結果將是大量的過時指標,並且會導致大量反社會行為。 因此,所有 debugfs 使用者 - 至少那些可以構建為模組的使用者 - 必須準備好刪除他們在其中建立的所有檔案和目錄。 可以使用以下命令刪除檔案或目錄

void debugfs_remove(struct dentry *dentry);

dentry 值可以為 NULL 或錯誤值,在這種情況下,將不會刪除任何內容。 請注意,此函式將遞迴刪除其下的所有檔案和目錄。 以前,debugfs_remove_recursive() 用於執行該任務,但此函式現在只是 debugfs_remove() 的別名。 debugfs_remove_recursive() 應被視為已棄用。