对象生命周期调试基础设施¶
- 作者:
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,因为这不是一个真正的修复。
已知错误和假设¶
无(敲木头)。