赞
踩
android的耗电问题,会涉及到wakelock部分。
https://blog.csdn.net/weixin_42322147/article/details/80469590
/** * struct wakeup_source - Representation of wakeup sources * * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. * @last_time: Monotonic clock when the wakeup source's was touched last time. * @prevent_sleep_time: Total time this source has been preventing autosleep. * @event_count: Number of signaled wakeup events. * @active_count: Number of times the wakeup source was activated. * @relax_count: Number of times the wakeup source was deactivated. * @expire_count: Number of times the wakeup source's timeout has expired. * @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. * @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source { const char *name; struct list_head entry; spinlock_t lock; struct timer_list timer; unsigned long timer_expires; ktime_t total_time; //wakeup source处于active的总时长 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; };
在root权限下,可以通过查看/d/wakeup_sources来查看wakelock的情况。
name active_count event_count wakeup_count expire_count active_since total_time
max_time last_change prevent_suspend_time
bluetooth_timer 64 64 0 0 0 3213 3001 69776 0
wakeup_sources这个节点的信息对分析耗电比较有用的数据有:
active_count, active_since, total_time。
active_count:上锁的次数
active_since:当前的wakelock已经持续的时间
total_time:这个锁开机以来一共lock的时间
上文中active_count=64次
上文中active_since=3213毫秒
上文中total_time=3001毫秒
当CPU无法睡下去时,很可能就是因为某个driver持有wakelock不放导致的。
这时可以从这个节点来分析,找出根源。
不过,这个节点只有root权限才能查看,这是限制条件。
一、 节点的创建
根据节点的名称,先查看节点生成的文件为kernel/msm-3.18/drivers/base/power/wakeup.c
static int __init wakeup_sources_debugfs_init(void){
wakeup_sources_stats_dentry = debugfs_create_file("wakeup_sources",
S_IRUGO, NULL, NULL, &wakeup_sources_stats_fops); //创建"/d/wakeup_sources"。
return 0;
}
二、节点操作的相关函数
static const struct file_operations wakeup_sources_stats_fops = {
.owner = THIS_MODULE,
.open = wakeup_sources_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
三、cat /d/wakeup_sources时调用的函数,wakeup_sources_stats_open
static int wakeup_sources_stats_open(struct inode *inode, struct file *file){
return single_open(file, wakeup_sources_stats_show, NULL);
}
再看看wakeup_sources_stats_show。
/** * wakeup_sources_stats_show - Print wakeup sources statistics information. * @m: seq_file to print the statistics into. */ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) { struct wakeup_source *ws; seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t" "expire_count\tactive_since\ttotal_time\tmax_time\t" "last_change\tprevent_suspend_time\n"); //这就是上面的第一行。 rcu_read_lock(); list_for_each_entry_rcu(ws, &wakeup_sources, entry) //互斥遍历wakeup_sources,每次取出其中之一ws. print_wakeup_source_stats(m, ws); //打印出这个ws的相关信息 rcu_read_unlock(); return 0; }
再看看print_wakeup_source_stats:
/** * print_wakeup_source_stats - Print wakeup source statistics information. * @m: seq_file to print the statistics into. * @ws: Wakeup source object to print the statistics for. */ static int print_wakeup_source_stats(struct seq_file *m, struct wakeup_source *ws) { unsigned long flags; ktime_t total_time; ktime_t max_time; unsigned long active_count; ktime_t active_time; ktime_t prevent_sleep_time; int ret; spin_lock_irqsave(&ws->lock, flags); total_time = ws->total_time; //之前的total_time max_time = ws->max_time; prevent_sleep_time = ws->prevent_sleep_time; active_count = ws->active_count; if (ws->active) { //如果这个wakelock还在,没有释放掉 ktime_t now = ktime_get(); active_time = ktime_sub(now, ws->last_time); //active_time就是开始上锁到目前时间差 total_time = ktime_add(total_time, active_time); // total_time 就是上一次的total_time加上active_time if (active_time.tv64 > max_time.tv64) max_time = active_time; if (ws->autosleep_enabled) prevent_sleep_time = ktime_add(prevent_sleep_time, ktime_sub(now, ws->start_prevent_time)); } else { active_time = ktime_set(0, 0); } ret = seq_printf(m, "%-32s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", ws->name, active_count, ws->event_count, ws->wakeup_count, ws->expire_count, ktime_to_ms(active_time), ktime_to_ms(total_time), ktime_to_ms(max_time), ktime_to_ms(ws->last_time), ktime_to_ms(prevent_sleep_time)); //结果 spin_unlock_irqrestore(&ws->lock, flags); return ret; }
四、wakeup_sources,一个当前文件中的全局list。
在init一个wakelock的时候添加。
上面遍历的 wakeup_sources,本文件中的一个全局链表。
很容易猜测到,每次申请一个wake_lock时,都会添加一个item到这个链表中。
看看这个全局的list的声明和初始化。
static LIST_HEAD(wakeup_sources); //初始化。
接下来看这个list的添加。想要使用wakelock,肯定是要先调用下面的init函数。我们在kernel中声明的wake_lock结构都有wakeup_source结构成员。后面更多的是用这个wakeup_source结构来管理。
static inline void wake_lock_init(struct wake_lock *lock, int type, const char *name) {
wakeup_source_init(&lock->ws, name);
}
接着看被调用的wakeup_source_init。
static inline void wakeup_source_init(struct wakeup_source *ws, const char *name) {
wakeup_source_prepare(ws, name); //准备工作
wakeup_source_add(ws); //真正的添加。
}
继续看wakeup_source_add.
/** * wakeup_source_add - Add given object to the list of wakeup sources. * @ws: Wakeup source object to add to the list. */ void wakeup_source_add(struct wakeup_source *ws) { unsigned long flags; if (WARN_ON(!ws)) return; spin_lock_init(&ws->lock); setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws); ws->active = false; //初始化为非active ws->last_time = ktime_get(); //当前时间。 spin_lock_irqsave(&events_lock, flags); list_add_rcu(&ws->entry, &wakeup_sources); //添加到wakeup_sources spin_unlock_irqrestore(&events_lock, flags); } EXPORT_SYMBOL_GPL(wakeup_source_add);
五、wakelock上锁时的操作
上锁时,都会调用下面的wake_lock函数。
static inline void wake_lock(struct wake_lock *lock){
__pm_stay_awake(&lock->ws);
}
继续看__pm_stay_awake。
/** * __pm_stay_awake - Notify the PM core of a wakeup event. * @ws: Wakeup source object associated with the source of the event. * * It is safe to call this function from interrupt context. */ void __pm_stay_awake(struct wakeup_source *ws) { unsigned long flags; if (!ws) return; spin_lock_irqsave(&ws->lock, flags); wakeup_source_report_event(ws); //主要函数。 del_timer(&ws->timer); ws->timer_expires = 0; spin_unlock_irqrestore(&ws->lock, flags); } EXPORT_SYMBOL_GPL(__pm_stay_awake);
继续看wakeup_source_report_event。
/**
* wakeup_source_report_event - Report wakeup event using the given source.
* @ws: Wakeup source to report the event for.
*/
static void wakeup_source_report_event(struct wakeup_source *ws)
{
ws->event_count++; //event_count计数
/* This is racy, but the counter is approximate anyway. */
if (events_check_enabled)
ws->wakeup_count++;//wakeup_count计数
if (!ws->active) //如果是非active状态
wakeup_source_activate(ws); //那就变成active
}
接着看wakeup_source_activate.
/** * wakup_source_activate - Mark given wakeup source as active. * @ws: Wakeup source to handle. * * Update the @ws' statistics and, if @ws has just been activated, notify the PM * core of the event by incrementing the counter of of wakeup events being * processed. */ static void wakeup_source_activate(struct wakeup_source *ws) { unsigned int cec; /* * active wakeup source should bring the system * out of PM_SUSPEND_FREEZE state */ freeze_wake(); //保证上锁期间CPU不会睡下去 ws->active = true; ws->active_count++; //active_count计数 ws->last_time = ktime_get(); //这时的时间,也就是开始上锁的时间 if (ws->autosleep_enabled) ws->start_prevent_time = ws->last_time; /* Increment the counter of events in progress. */ cec = atomic_inc_return(&combined_event_count); trace_wakeup_source_activate(ws->name, cec); }
六、wakelock释放时的操作
在kernel wakelock释放的时候,都会调用下面的wake_unlock函数。
static inline void wake_unlock(struct wake_lock *lock)
{
__pm_relax(&lock->ws);
}
继续看看__pm_relax。
/** * __pm_relax - Notify the PM core that processing of a wakeup event has ended. * @ws: Wakeup source object associated with the source of the event. * * Call this function for wakeup events whose processing started with calling * __pm_stay_awake(). * * It is safe to call it from interrupt context. */ void __pm_relax(struct wakeup_source *ws) { unsigned long flags; if (!ws) return; spin_lock_irqsave(&ws->lock, flags); if (ws->active) //如果目前仍是active,那就要释放掉 wakeup_source_deactivate(ws); spin_unlock_irqrestore(&ws->lock, flags); } EXPORT_SYMBOL_GPL(__pm_relax);
再看wakeup_source_deactivate。
/** * wakup_source_deactivate - Mark given wakeup source as inactive. * @ws: Wakeup source to handle. * * Update the @ws' statistics and notify the PM core that the wakeup source has * become inactive by decrementing the counter of wakeup events being processed * and incrementing the counter of registered wakeup events. */ static void wakeup_source_deactivate(struct wakeup_source *ws) { unsigned int cnt, inpr, cec; ktime_t duration; ktime_t now; ws->relax_count++; /* * __pm_relax() may be called directly or from a timer function. * If it is called directly right after the timer function has been * started, but before the timer function calls __pm_relax(), it is * possible that __pm_stay_awake() will be called in the meantime and * will set ws->active. Then, ws->active may be cleared immediately * by the __pm_relax() called from the timer function, but in such a * case ws->relax_count will be different from ws->active_count. */ if (ws->relax_count != ws->active_count) { ws->relax_count--; return; } ws->active = false; now = ktime_get(); duration = ktime_sub(now, ws->last_time); //完整上锁的时间 ws->total_time = ktime_add(ws->total_time, duration); //这把所从init之后所有上锁的时间总和 if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) ws->max_time = duration; ws->last_time = now; del_timer(&ws->timer); ws->timer_expires = 0; if (ws->autosleep_enabled) update_prevent_sleep_time(ws, now); /* * Increment the counter of registered wakeup events and decrement the * couter of wakeup events in progress simultaneously. */ cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count); trace_wakeup_source_deactivate(ws->name, cec); split_counters(&cnt, &inpr); if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) wake_up(&wakeup_count_wait_queue); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。