物件生命週期除錯基礎設施

作者:

Thomas Gleixner

介紹

debugobjects 是一個通用基礎設施,用於跟蹤核心物件的生命週期並驗證對這些物件的操作。

debugobjects 對於檢查以下錯誤模式非常有用:

  • 啟用未初始化的物件

  • 初始化活躍物件

  • 使用已釋放/已銷燬的物件

debugobjects 不會改變實際物件的資料結構,因此可以在編譯時對其進行編譯,執行時影響最小,並可以透過核心命令列選項按需啟用。

如何使用 debugobjects

核心子系統需要提供一個數據結構來描述物件型別,並在適當的地方新增對除錯程式碼的呼叫。描述物件型別的資料結構至少需要物件型別的名稱。可以選擇並應該提供可選函式來修復檢測到的問題,以便核心可以繼續工作,並且可以從即時系統而不是透過序列埠控制檯和監視器中的堆疊跟蹤記錄進行硬核除錯來檢索除錯資訊。

debugobjects 提供的除錯呼叫有:

  • debug_object_init

  • debug_object_init_on_stack

  • debug_object_activate

  • debug_object_deactivate

  • debug_object_destroy

  • debug_object_free

  • debug_object_assert_init

每個函式都接受實際物件的地址和指向物件型別特定除錯描述結構的指標。

每個檢測到的錯誤都會在統計資訊中報告,並且有限數量的錯誤會透過 printk 打印出來,包括完整的堆疊跟蹤。

統計資訊可透過 /sys/kernel/debug/debug_objects/stats 獲取。它們提供有關警告數量和成功修復數量的資訊,以及有關內部跟蹤物件使用情況和內部跟蹤物件池狀態的資訊。

除錯函式

void debug_object_init(void *addr, const struct debug_obj_descr *descr)

物件初始化時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標

每當呼叫實際物件的初始化函式時,都會呼叫此函式。

當實際物件已被 debugobjects 跟蹤時,會檢查該物件是否可以初始化。不允許初始化活躍和已銷燬的物件。當 debugobjects 檢測到錯誤時,如果呼叫者提供了物件型別描述結構中的 fixup_init 函式,則會呼叫該函式。修復函式可以在實際物件初始化之前糾正問題。例如,它可以停用一個活躍物件,以防止對子系統造成損害。

當實際物件尚未被 debugobjects 跟蹤時,debugobjects 會為實際物件分配一個跟蹤器物件,並將跟蹤器物件的狀態設定為 ODEBUG_STATE_INIT。它會驗證該物件是否不在呼叫者的堆疊上。如果它在呼叫者的堆疊上,則會 printk 列印有限數量的警告,包括完整的堆疊跟蹤。呼叫程式碼必須使用 debug_object_init_on_stack() 並在離開分配它的函式之前刪除該物件。請參閱下一節。

void debug_object_init_on_stack(void *addr, const struct debug_obj_descr *descr)

堆疊上物件初始化時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標

每當呼叫位於堆疊上的實際物件的初始化函式時,都會呼叫此函式。

當實際物件已被 debugobjects 跟蹤時,會檢查該物件是否可以初始化。不允許初始化活躍和已銷燬的物件。當 debugobjects 檢測到錯誤時,如果呼叫者提供了物件型別描述結構中的 fixup_init 函式,則會呼叫該函式。修復函式可以在實際物件初始化之前糾正問題。例如,它可以停用一個活躍物件,以防止對子系統造成損害。

當實際物件尚未被 debugobjects 跟蹤時,debugobjects 會為實際物件分配一個跟蹤器物件,並將跟蹤器物件的狀態設定為 ODEBUG_STATE_INIT。它會驗證該物件是否在呼叫者的堆疊上。

位於堆疊上的物件必須在分配該物件的函式返回之前,透過呼叫 debug_object_free() 從跟蹤器中移除。否則,我們會繼續跟蹤陳舊的物件。

int debug_object_activate(void *addr, const struct debug_obj_descr *descr)

物件啟用時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標。成功返回 0,檢查失敗返回 -EINVAL。

每當呼叫實際物件的啟用函式時,都會呼叫此函式。

當實際物件已被 debugobjects 跟蹤時,會檢查該物件是否可以啟用。不允許啟用活躍和已銷燬的物件。當 debugobjects 檢測到錯誤時,如果呼叫者提供了物件型別描述結構中的 fixup_activate 函式,則會呼叫該函式。修復函式可以在實際物件啟用之前糾正問題。例如,它可以停用一個活躍物件,以防止對子系統造成損害。

當實際物件尚未被 debugobjects 跟蹤時,如果 fixup_activate 函式可用,則會呼叫該函式。這對於允許合法啟用靜態分配和初始化的物件是必要的。修復函式檢查物件是否有效,並呼叫 debug_objects_init() 函式來初始化此物件的跟蹤。

當啟用合法時,關聯跟蹤器物件的狀態設定為 ODEBUG_STATE_ACTIVE。

void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr)

物件停用時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標

每當呼叫實際物件的停用函式時,都會呼叫此函式。

當實際物件被 debugobjects 跟蹤時,會檢查該物件是否可以停用。不允許停用未跟蹤或已銷燬的物件。

當停用合法時,關聯跟蹤器物件的狀態設定為 ODEBUG_STATE_INACTIVE。

void debug_object_destroy(void *addr, const struct debug_obj_descr *descr)

物件銷燬時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標

呼叫此函式以標記物件已銷燬。這對於防止使用記憶體中仍然可用的無效物件非常有用:無論是靜態分配的物件還是稍後才釋放的物件。

當實際物件被 debugobjects 跟蹤時,會檢查該物件是否可以銷燬。不允許銷燬活躍和已銷燬的物件。當 debugobjects 檢測到錯誤時,如果呼叫者提供了物件型別描述結構中的 fixup_destroy 函式,則會呼叫該函式。修復函式可以在實際物件銷燬之前糾正問題。例如,它可以停用一個活躍物件,以防止對子系統造成損害。

當銷燬合法時,關聯跟蹤器物件的狀態設定為 ODEBUG_STATE_DESTROYED。

void debug_object_free(void *addr, const struct debug_obj_descr *descr)

物件釋放時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標

在釋放物件之前呼叫此函式。

當實際物件被 debugobjects 跟蹤時,會檢查該物件是否可以釋放。不允許釋放活躍物件。當 debugobjects 檢測到錯誤時,如果呼叫者提供了物件型別描述結構中的 fixup_free 函式,則會呼叫該函式。修復函式可以在實際物件釋放之前糾正問題。例如,它可以停用一個活躍物件,以防止對子系統造成損害。

請注意,debug_object_free 會從跟蹤器中移除物件。物件後續的使用將由其他除錯檢查檢測到。

void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)

物件應初始化時的除錯檢查

引數

void *addr

物件的地址

const struct debug_obj_descr *descr

指向物件特定除錯描述結構的指標

呼叫此函式以斷言物件已初始化。

當實際物件未被 debugobjects 跟蹤時,它會呼叫呼叫者提供的物件型別描述結構中的 fixup_assert_init,並使用硬編碼的物件狀態 ODEBUG_NOT_AVAILABLE。修復函式可以透過呼叫 debug_object_init 和其他特定初始化函式來糾正問題。

當實際物件已被 debugobjects 跟蹤時,它會被忽略。

修復函式

除錯物件型別描述結構

struct debug_obj

跟蹤物件的表示

定義:

struct debug_obj {
    struct hlist_node               node;
    enum debug_obj_state            state;
    unsigned int                    astate;
    union {
        void *object;
        struct hlist_node       *batch_last;
    };
    const struct debug_obj_descr *descr;
};

成員

node

hlist 節點,用於將物件連結到跟蹤器列表

state

跟蹤物件狀態

astate

當前活躍狀態

{unnamed_union}

匿名

object

指向實際物件的指標

batch_last

指向批次中最後一個 hlist 節點的指標

descr

指向物件型別特定除錯描述結構的指標

struct debug_obj_descr

物件型別特定除錯描述結構

定義:

struct debug_obj_descr {
    const char              *name;
    void *(*debug_hint)(void *addr);
    bool (*is_static_object)(void *addr);
    bool (*fixup_init)(void *addr, enum debug_obj_state state);
    bool (*fixup_activate)(void *addr, enum debug_obj_state state);
    bool (*fixup_destroy)(void *addr, enum debug_obj_state state);
    bool (*fixup_free)(void *addr, enum debug_obj_state state);
    bool (*fixup_assert_init)(void *addr, enum debug_obj_state state);
};

成員

name

物件型別名稱

debug_hint

返回關聯核心符號的地址的函式,以允許識別物件

is_static_object

如果物件是靜態的,則返回 true,否則返回 false

fixup_init

修復函式,當初始化檢查失敗時呼叫。所有修復函式如果修復成功必須返回 true,否則返回 false

fixup_activate

修復函式,當啟用檢查失敗時呼叫

fixup_destroy

修復函式,當銷燬檢查失敗時呼叫

fixup_free

修復函式,當釋放檢查失敗時呼叫

fixup_assert_init

修復函式,當斷言初始化檢查失敗時呼叫

fixup_init

每當在 debug_object_init 中檢測到問題時,都會從除錯程式碼中呼叫此函式。該函式接受物件的地址和跟蹤器中當前記錄的狀態。

當物件狀態為以下情況時,從 debug_object_init 呼叫:

  • ODEBUG_STATE_ACTIVE

當修復成功時,該函式返回 true,否則返回 false。返回值用於更新統計資訊。

請注意,修復函式在修復損壞後需要再次呼叫 debug_object_init() 函式,以保持狀態一致。

fixup_activate

每當在 debug_object_activate 中檢測到問題時,都會從除錯程式碼中呼叫此函式。

當物件狀態為以下情況時,從 debug_object_activate 呼叫:

  • ODEBUG_STATE_NOTAVAILABLE

  • ODEBUG_STATE_ACTIVE

當修復成功時,該函式返回 true,否則返回 false。返回值用於更新統計資訊。

請注意,修復函式在修復損壞後需要再次呼叫 debug_object_activate() 函式,以保持狀態一致。

靜態初始化物件的啟用是一個特殊情況。當 debug_object_activate() 沒有為該物件地址跟蹤物件時,會呼叫 fixup_activate(),物件狀態為 ODEBUG_STATE_NOTAVAILABLE。修復函式需要檢查這是否是靜態初始化物件的合法情況。如果是,它會呼叫 debug_object_init()debug_object_activate() 使物件被跟蹤器知曉並標記為活躍。在這種情況下,函式應該返回 false,因為這不是一個真正的修復。

fixup_destroy

每當在 debug_object_destroy 中檢測到問題時,都會從除錯程式碼中呼叫此函式。

當物件狀態為以下情況時,從 debug_object_destroy 呼叫:

  • ODEBUG_STATE_ACTIVE

當修復成功時,該函式返回 true,否則返回 false。返回值用於更新統計資訊。

fixup_free

每當在 debug_object_free 中檢測到問題時,都會從除錯程式碼中呼叫此函式。此外,當 debug_check_no_obj_freed() 健全性檢查檢測到活躍物件時,它也可以從 kfree/vfree 中的除錯檢查呼叫。

當物件狀態為以下情況時,從 debug_object_free() 或 debug_check_no_obj_freed() 呼叫:

  • ODEBUG_STATE_ACTIVE

當修復成功時,該函式返回 true,否則返回 false。返回值用於更新統計資訊。

fixup_assert_init

每當在 debug_object_assert_init 中檢測到問題時,都會從除錯程式碼中呼叫此函式。

當在除錯桶中未找到物件時,從 debug_object_assert_init() 呼叫,硬編碼狀態為 ODEBUG_STATE_NOTAVAILABLE。

當修復成功時,該函式返回 true,否則返回 false。返回值用於更新統計資訊。

請注意,此函式應確保在返回之前呼叫 debug_object_init()

靜態初始化物件的處理是一個特殊情況。修復函式應檢查這是否是靜態初始化物件的合法情況。在這種情況下,只需呼叫 debug_object_init() 使物件被跟蹤器知曉。然後函式應該返回 false,因為這不是一個真正的修復。

已知錯誤和假設

無(敲木頭)。