리눅스 커널의 슬럽 관련 디버깅 피쳐를 켜 놓으면, struct track 구조체로 슬럽 오브젝트를 할당하거나 해제할 때의 콜 스택 정보를 저장합니다. 이를 처리하는 핵심 함수는 set_track() 함수인데, 이 함수에 마지막으로 전달되는 인자는 addr입니다.
다음은 set_track() 함수의 구현부인데, 볼드체로 표기된 부분을 봅시다.
https://elixir.bootlin.com/linux/v4.19.30/source/mm/slub.c
static void set_track(struct kmem_cache *s, void *object,
enum track_item alloc, unsigned long addr)
{
struct track *p = get_track(s, object, alloc);
if (addr) {
...
p->addr = addr;
p->cpu = smp_processor_id();
p->pid = current->pid;
p->when = jiffies;
} else
memset(p, 0, sizeof(struct track));
}
코드를 분석한 결과 다음과 같은 사실을 알 수 있습니다.
"addr의 정체는 _RET_IP_ 매크로이며, 슬랩 오브젝트를 할당하거나 해제하는 코드의 주소를 저장한다."
참고로 _RET_IP_의 정체는 GCC 컴파일러에서 제공하는 __builtin_return_address(0) 함수입니다.
#define _RET_IP_ (unsigned long)__builtin_return_address(0)
다음은 set_track() 함수의 마지막 인자로 전달되는 addr의 함수 콜 플로우이니,
눈을 크게 뜨고 매직 아이를 보듯이 확인합시다.
슬랩 오브젝트를 할당하는 과정
1번째 패스
static __always_inline void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t flags, int node)
{
return kmem_cache_alloc(s, flags);
}
void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
{
void *ret = slab_alloc_node(s, gfpflags, node, _RET_IP_);
trace_kmem_cache_alloc_node(_RET_IP_, ret,
s->object_size, s->size, gfpflags, node);
return ret;
}
2번째 패스
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
{
void *ret = slab_alloc(s, gfpflags, _RET_IP_);
static __always_inline void *slab_alloc(struct kmem_cache *s,
gfp_t gfpflags, unsigned long addr)
{
return slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr);
}
slab_alloc_node 이후 인자
static __always_inline void *slab_alloc_node(struct kmem_cache *s,
gfp_t gfpflags, int node, unsigned long addr)
static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
unsigned long addr, struct kmem_cache_cpu *c)
static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
unsigned long addr, struct kmem_cache_cpu *c)
alloc_debug_processing(s, page, freelist, addr);
set_track(s, object, TRACK_ALLOC, addr);
슬랩 오브젝트를 해제할 때의 과정
https://elixir.bootlin.com/linux/v4.19.30/source/mm/slub.c
void kfree(const void *x)
- void *object = (void *)x;
slab_free(page->slab_cache, page, object, NULL, 1, _RET_IP_);
static __always_inline void slab_free(struct kmem_cache *s, struct page *page,
void *head, void *tail, int cnt,
unsigned long addr)
do_slab_free(s, page, head, tail, cnt, addr);
__slab_free(s, page, head, tail_obj, cnt, addr);
free_debug_processing(s, page, head, tail, cnt, addr);
set_track(s, object, TRACK_FREE, addr);
이 정도로 분석해 놓고, 나중에 슬럽 오브젝트를 디버깅할 때 확인되는 정보가 있으면 더 업데이트하겠습니다.
최근 덧글