slub set_track

发布时间:2026/10/28 19:04:12
slub set_track

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_FREE
addr调用地址(返回地址)
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 }

逻辑:

  1. #ifdef CONFIG_SLUB_DEBUG:未编译调试支持,函数体为空,调用点被完全优化掉。

  2. SLAB_STORE_USER检查:该缓存没开用户追踪,直接返回。

  3. 采集:handle = set_track_prepare(gfp_flags)。

  4. 写入:set_track_update(s, object, alloc, addr, handle)。

三、条件编译层次

set_track()处于三层门控之下:

CONFIG_SLUB_DEBUG // 编译期:是否有调试支持 └─ slub_debug_enabled // 运行时静态键:是否启用调试 └─ SLAB_STORE_USER // 缓存级标志:该缓存是否追踪用户 └─ 真正采集 + 写入
  • 未编译CONFIG_SLUB_DEBUG:set_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和条件编译保证未开调试时零开销,是“谁分配/释放了这个对象”追踪的起点。