본문 바로가기

[Debugging] Tips

[리눅스커널][Trace32] wakelock 디버깅 - container_of

 
이번 시간에는 wakeup_sources이란 링크드 리스트를 통해 wakelock 디버깅을 합시다.
이전에는 crash-utility를 썻는데 이번에는 Trace32를 쓰겠습니다.
 
개발자는 다양한 툴을 써야 한 가지 툴에 종속된 노예 개발자가 되는 것을 피할 수 있습니다.
또한 각 툴의 장점을 잘 활용할 수도 있습니다.
 
먼저, 다음 T32 명령어를 입력해서 offsetof와 container_of 매크로를 생성합시다.
sYmbol.NEW.MACRO offsetof(type,member) ((int)(&((type*)0)->member))
sYmbol.NEW.MACRO container_of(ptr,type,member) ((type *)((char *)(ptr)-offsetof(type,member)))
 
wakelock을 잡은 모듈들은 wakeup_sources 이란 전역 변수에 등록돼 있습니다.
우선 wakeup_sources 변수를 봅시다.
v.v %h %t wakeup_sources
  (static struct list_head) wakeup_sources = (
    (struct list_head *) next = 0xFFFFFFC5FD92EA88 -> (
      (struct list_head *) next = 0xFFFFFFC5FD92FC88 -> (
        (struct list_head *) next = 0xFFFFFFC541226488,
        (struct list_head *) prev = 0xFFFFFFC5FD92EA88),
      (struct list_head *) prev = 0xFFFFFF97D92B4D00),
    (struct list_head *) prev = 0xFFFFFFC62D3EDB88)
 
계속 struct list_head->next로 리스트들이 연결돼 있습니다.
 
이제 앞에서 생성한 container_of 매크로를 쓸 순간입니다. 다음과 같이 명령어를 입력합시다.
v.v %h %t %s  container_of(0xFFFFFFC5FD92EA88,struct wakeup_source,entry)
  (struct wakeup_source *) container_of(0xFFFFFFC5FD92EA88,struct wakeup_source,entry) = 0xFFFFFFC5FD92EA80 -> (
    (char *) name = 0xFFFFFFC55DEE3280 -> "ipc00001269_1988_HwBinder:682_1",
    (struct list_head) entry = ((struct list_head *) next = 0xFFFFFFC5FD92FC88, (struct list_head *) prev = 0xFFF
    (spinlock_t) lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t) raw_lock = ((u16) owner = 0x298F, (u16)
    (struct wake_irq *) wakeirq = 0x0,
    (struct timer_list) timer = ((struct hlist_node) entry = ((struct hlist_node *) next = 0x0, (struct hlist_nod
    (long unsigned int) timer_expires = 0x0,
    (ktime_t) total_time = ((s64) tv64 = 0x0),
    (ktime_t) max_time = ((s64) tv64 = 0x0),
    (ktime_t) last_time = ((s64) tv64 = 0x000040E1FC6B0DF2),
    (ktime_t) start_prevent_time = ((s64) tv64 = 0x0),
    (ktime_t) prevent_sleep_time = ((s64) tv64 = 0x0),
    (long unsigned int) event_count = 0x0,
    (long unsigned int) active_count = 0x0,
    (long unsigned int) relax_count = 0x0,
    (long unsigned int) expire_count = 0x0,
    (long unsigned int) wakeup_count = 0x0,
    (long unsigned int) pending_count = 0x0,
    (bool:1) active = FALSE,
    (bool:1) autosleep_enabled = FALSE)
 
위 멤버 변수 중 active 변수가 FALSE이니 "ipc00001269_1988_HwBinder:682_1"이란 wakeup source가 wakelock을 잡고 있지 않습니다.
 
v.v %h %t %s  container_of(0xFFFFFFC5FD92EA88,struct wakeup_source,entry)
 
위와 같은 명령어를 쓴 이유는, struct wakeup_source이란 구조체에 entry이란 struct list_head 타입 멤버 변수가
wakeup_sources이란 링크드 리스트 전역 변수 wakeup_sources->next로 등록됐기 때문입니다.
 
해당 구조체를 봅시다.
struct wakeup_source {
const char  *name;
struct list_head entry;
spinlock_t lock;
struct wake_irq *wakeirq;
struct timer_list timer;
unsigned long timer_expires;
ktime_t total_time;
ktime_t max_time;
ktime_t last_time;
ktime_t start_prevent_time;
ktime_t prevent_sleep_time;
unsigned long event_count;
unsigned long active_count;
unsigned long relax_count;
unsigned long expire_count;
unsigned long wakeup_count;
bool active:1;
bool autosleep_enabled:1;
};
 
쉽게 설명하면 아래 방식으로 wakeup_source가 wakeup_sources 리스트에 등록합니다.
1st wakeup source 등록
wakeup_sources->next = struct wakeup_source->entry
 
2nd wakeup source등록
wakeup_sources->next->next = struct wakeup_source->entry
 
개발자분들이여, 임베디드 동네의 최강의 툴인 Trace32를 잘 활용해서 일찍 퇴근합시다.