본문 바로가기

[Debugging] Tips

[Kernel] wakelock debug patch

 

안드로이드에서 wake lock이란 기능이 있습니다. 그런데 대부분 wake lock을 어떤 모듈이 잡고 있어서 슬립에 못 들어가는 문제가 생기죠. 이럴 때 프로파일링하면 좋은 디버그 패치를 소개합니다.

diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
old mode 100644
new mode 100755
index 7dbfe1a..2ca0964
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -54,6 +54,7 @@ static DEFINE_SPINLOCK(events_lock);
 static void pm_wakeup_timer_fn(unsigned long data);
 
 static LIST_HEAD(wakeup_sources);
+static LIST_HEAD(wakeup_sources_shadow);
 
 static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
 
@@ -134,6 +135,17 @@ static void wakeup_source_destroy_cb(struct rcu_head *head)
  wakeup_source_destroy(container_of(head, struct wakeup_source, rcu));
 }
 
+struct wakeup_source_shadow {
+ struct list_head entry;
+ const char *name;
+ struct task_struct *p;
+ struct wakeup_source *ws;
+ ktime_t add_ts;
+ ktime_t rmv_ts;
+ int ws_event; /* 1 for add, 2 for remove */
+}; 
+
+
 /**
  * wakeup_source_add - Add given object to the list of wakeup sources.
  * @ws: Wakeup source object to add to the list.
@@ -141,7 +153,8 @@ static void wakeup_source_destroy_cb(struct rcu_head *head)
 void wakeup_source_add(struct wakeup_source *ws)
 {
  unsigned long flags;
-
+ struct wakeup_source_shadow *ws_sh;
+
  if (WARN_ON(!ws))
  return;
 
@@ -152,6 +165,19 @@ void wakeup_source_add(struct wakeup_source *ws)
 
  spin_lock_irqsave(&events_lock, flags);
  list_add_rcu(&ws->entry, &wakeup_sources);
+ if (!strncmp(ws->name, "wlan", strlen("wlan"))) {
+ pr_err("[%s] add hit : %p, %s\n", __func__, ws, ws->name);
+ ws_sh = kmalloc(sizeof(*ws_sh), GFP_ATOMIC);
+ if (ws_sh) {
+ ws_sh->ws = ws;
+ ws_sh->p = current;
+ ws_sh->name = ws->name;
+ ws_sh->add_ts = ws_sh->rmv_ts = ws->last_time;
+ ws_sh->ws_event = 1;
+ list_add_rcu(&ws_sh->entry, &wakeup_sources_shadow);
+ dump_stack();
+ }
+ }
  spin_unlock_irqrestore(&events_lock, flags);
 }
 EXPORT_SYMBOL_GPL(wakeup_source_add);
@@ -163,12 +189,25 @@ EXPORT_SYMBOL_GPL(wakeup_source_add);
 void wakeup_source_remove(struct wakeup_source *ws)
 {
  unsigned long flags;
-
+ struct wakeup_source_shadow *ws_sh;
+
  if (WARN_ON(!ws))
  return;
 
  spin_lock_irqsave(&events_lock, flags);
  list_del_rcu(&ws->entry);
+ if (!strncmp(ws->name, "wlan", strlen("wlan"))) {
+ pr_err("[%s] rmv hit : %p, %s\n", __func__, ws, ws->name);
+ list_for_each_entry(ws_sh, &wakeup_sources_shadow, entry) {
+ if (ws_sh->ws == ws) {
+ ws_sh->rmv_ts = ktime_get();
+ ws_sh->ws_event = 2;
+ ws_sh->p = current;
+ dump_stack();
+ break;
+ }
+ }
+ }
  spin_unlock_irqrestore(&events_lock, flags);
  synchronize_rcu();
 }