SLUB track 机制的统一入口把set_track_prepare()采集和set_track_update()写入串起来并根据配置做条件编译裁剪。一、函数签名static __always_inline void set_track(struct kmem_cache *s, void *object, enum track_item alloc, unsigned long addr, gfp_t gfp_flags)参数含义s缓存object对象指针allocTRACK_ALLOC或TRACK_FREEaddr调用地址返回地址gfp_flags传给set_track_prepare()用于 stack depot 存储时的内存分配__always_inline强制内联调用点通常已在外层做过SLAB_STORE_USER判断内联后能把整个函数优化掉或压成几条指令。二、典型实现static __always_inline void set_track(struct kmem_cache *s, void *object, enum track_item alloc, unsigned long addr, gfp_t gfp_flags) { #ifdef CONFIG_SLUB_DEBUG depot_stack_handle_t handle; if (!kmem_cache_debug_flags(s, SLAB_STORE_USER)) return; handle set_track_prepare(gfp_flags); set_track_update(s, object, alloc, addr, handle); #endif }逻辑#ifdef CONFIG_SLUB_DEBUG未编译调试支持函数体为空调用点被完全优化掉。SLAB_STORE_USER检查该缓存没开用户追踪直接返回。采集handle set_track_prepare(gfp_flags)。写入set_track_update(s, object, alloc, addr, handle)。三、条件编译层次set_track()处于三层门控之下CONFIG_SLUB_DEBUG // 编译期是否有调试支持 └─ slub_debug_enabled // 运行时静态键是否启用调试 └─ SLAB_STORE_USER // 缓存级标志该缓存是否追踪用户 └─ 真正采集 写入未编译CONFIG_SLUB_DEBUGset_track()是空函数零开销。编译了但没开kmem_cache_debug_flags()因静态键为 false 返回 false快速返回。开了但该缓存没开SLAB_STORE_USER同样快速返回。都满足才付出采集调用栈的代价。这和前面kmem_cache_debug()、slub_debug_enabled的设计一脉相承默认零开销开启才付代价。四、gfp_flags的作用handle set_track_prepare(gfp_flags);gfp_flags一路传给stack_depot_save()分配路径通常传GFP_ATOMIC因为对象分配可能在原子上下文不能睡眠。释放路径可能传GFP_ATOMIC或GFP_KERNEL取决于调用点上下文。stack depot 首次遇到新调用栈时需要用这个标志分配内存若分配失败句柄可能为 0但addr仍记录追踪不完全丢失。五、调用点set_track()在对象的分配和释放调试路径里被调用// 分配 alloc_debug_processing(s, slab, object, addr) └─ set_track(s, object, TRACK_ALLOC, addr, GFP_ATOMIC); // 释放 free_debug_processing(s, slab, object, addr) └─ set_track(s, object, TRACK_FREE, addr, GFP_ATOMIC);之后读取用get_track()struct track *t get_track(s, object, TRACK_ALLOC);六、与三个子函数的关系set_track() ← 统一入口本函数 │ 检查 SLAB_STORE_USER │ ├─ set_track_prepare(gfp_flags) ← 采集stack_trace_save stack_depot_save │ └─ 返回 depot_stack_handle_t │ └─ set_track_update(s, object, alloc, addr, handle) ← 写入填 struct track └─ get_track() 定位槽位函数职责是否有分配set_track_prepare采集调用栈存 depot可能首次见该栈set_track_update写入 track 字段无set_track门控 串联两者取决于 prepare七、__always_inline的意义调用点通常已经在外层判断过调试标志set_track()内联后条件检查和两次函数调用能被合并优化。未开调试时整个函数体被#ifdef去掉内联后调用点变成空零指令。强制内联避免“空函数调用”的残留开销。八、与前面知识的呼应前面讲过这里体现set_track_prepare()采集阶段set_track_update()写入阶段get_track()定位槽位SLAB_STORE_USER门控条件kmem_cache_debug_flags()检查标志CONFIG_SLUB_DEBUG编译期总开关struct track最终写入的结构九、一句话总结set_track(s, object, alloc, addr, gfp_flags)是 SLUB track 机制的统一入口在CONFIG_SLUB_DEBUG和SLAB_STORE_USER双重门控下调用set_track_prepare()采集调用栈生成句柄再调用set_track_update()把地址、句柄、CPU、PID、时间戳写入struct track。它用__always_inline和条件编译保证未开调试时零开销是“谁分配/释放了这个对象”追踪的起点。