리눅스 커널 코드를 분석하다 보면 아래 container_of 매크로를 자주 볼수 있어요.
void t21142_media_task(struct work_struct *work)
{
struct tulip_private *tp =
container_of(work, struct tulip_private, media_work);
struct net_device *dev = tp->dev;
 
그럼 T32으로 코어 덤프를 분석 할 때 위 매크로를 그대로 가져다 쓸 수 있는 방법을 소개하고자 합니다. 아주 간단해요. T32로 아래 명령어를 치면 되죠.
sYmbol.NEW.MACRO offsetof(type,member) ((int)(&((type*)0)->member))
sYmbol.NEW.MACRO container_of(ptr,type,member) ((type *)((char *)(ptr)-offsetof(type,member)))
 
 
예를 들어, 아래와 같이 mutex lock 변수가 있다고 가정해 보면. 
struct mutex.wait_list는 mutex lock을 기다리는 다른 mutex lock의 struct mutex.wait_list 주소를 가르키고 있습니다. 
(꼴을 보아하니 mutex deadlock같아 보이네요. "kworker/4:5" 란 프로세스가 mutex lock을 잠그고 잠들어 버린 것 같네요.)
  ( struct mutex *)0xFFFFFF981BC24540 = 0xFFFFFF981BC24540 -> (
    count = (counter = -1),
    wait_lock = (rlock = (raw_lock = (owner = 1445, next = 1445), magic = 3735899821, owner_cpu = 42
    wait_list = (
      next = 0xFFFFFFD388F4BB78 -> (
        next = 0xFFFFFF981BC24560 -> (
          next = 0xFFFFFFD388F4BB78,
//...
    owner = 0xFFFFFFD3F3085BC0, //<<--"kworker/4:5"
    magic = 0xFFFFFF981BC24540)
 
 
그럼 이 때 어떻게 디버깅을 할 수 있을까요? 이 경우, struct mutex 구조체에서 wait_list 멤버 오프셋 계산을 해야 해요. 이 값은 0x20이네요.
 
crash64> struct -o mutex
struct mutex {
   [0x0] atomic_t count;
   [0x8] spinlock_t wait_lock;
  [0x20] struct list_head wait_list;
  [0x30] struct task_struct *owner;
 
그리고 아래와 같이 명령어를 치면 되죠.    
v.v %s %t (struct mutex *)(0xFFFFFFD388F4BB78-0x20)
 
그런데 이 방법이 효율적인 것 같지는 않네요. 왜냐하면, 리눅스 커널에는 모래와 같이 엄청난 개수의 데이터 구조체가 있는데, 디버깅할 때 마다 맨날 오프셋을 계산하면 디버깅 하다 김이 빠지죠.
이럴 때, 아래와 같이 명령어를 치면 손 쉽게 디버깅을 할 수 있습니다.
v.v %all container_of(0xFFFFFFD388F4BB78,struct mutex,wait_list)
  (struct mutex *) container_of(0xFFFFFFD388F4BB78,struct mutex,wait_list) = 0xFFFFFFD388F4BB58 -> (
    (atomic_t) count = ((int) counter = 0x0),
    (spinlock_t) wait_lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t) raw_lock = ((u16) own
    (struct list_head) wait_list = ((struct list_head *) next = 0xFFFFFF981BC24560, (struct list_hea
    (struct task_struct *) owner = 0xFFFFFFD46F71AE00,  //<<-- "kworker/4:1" process
    (void *) magic = 0xFFFFFFD388F4BB78)
 
 

 

리눅스 커널이 제공하는 매크로를 그대로 사용할 수 있어서 좋고, 효율적으로 관련 변수를 확인할 수 있어서 좋습니다.

+ Recent posts