標籤:android待機
待機異常篇
待機異常篇待機異常
1: 按Power key後,連early_suspend都沒進。待機異常
2: 可以進early_suspend,但進不了suspend待機異常
3: 可以進suspend,但出現:PM: Some devices failed to suspend待機異常
4: 可以進入到suspend_enter,suspend流程走完了,但很快被喚醒待機異常
5: 可以進入到suspend_enter,也不被喚醒,但電流很大,CPU也較燙
關於early_suspend:
由於/sys/power/autosleep在處理suspend時,並沒有像/sys/power/state那樣,有對earlysuspend的相容處理,如:
Kernel/kernel/power/main.c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n){....#ifdef CONFIG_EARLYSUSPEND if (state == PM_SUSPEND_ON || valid_state(state)) { error = 0; request_suspend_state(state); }#else error = pm_suspend(state);#endif
進入request_suspend_state會先處理earlysuspend,然後再進入suspend。
而autosleep在處理時,是直接進入pm_suspend,不會去處理earlysuspend。
因此,不同平台處理earlysuspend的方式也不同,如Intel平台是通過將所有/sys/power/early_suspend/xxdevic/early_suspend檔案寫1使其對應的裝置進入earlysuspend(代碼路徑:hardware\libhardware\modules\power\),該操作發生在ANDROID待機流程的blankAllDisplays,在blankAllDisplays函數裡,會先調用nativeSetInteractive進入early_suspend,再調用nativeSetAutoSuspend進入suspend。
/sys/power/early_suspend/xxdevic/early_suspend 節點通過下面介面產生:
device_create_file(&dev->pdev->dev, &dev_attr_early_suspend);
register_early_suspend_device(&dev->pdev->dev);
待機異常1 :按Power key後,連early_suspend都沒進。
問題分析
這種情況發生在Android待機流程中的goToSleepInternal,由於不滿足待機條件而無法進入待機。
不能進入待機的條件:
private boolean goToSleepNoUpdateLocked(long eventTime, int reason) { if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) { return false; }
事件時間和電源狀態一般不會是異常的因素,因此引起異常的主要是mBootCompleted 和mSystemReady 兩個條件。
調試方法
mBootCompleted在收到Intent.ACTION_BOOT_COMPLETED廣播後置1,而Intent.ACTION_BOOT_COMPLETED廣播是在進入到Launcher後發出的,frameworks\base\services\java\com\android\server\am\ActivityManagerService.java,因此當系統啟動異常時進入不到Launcher,就無法待機;還有一種情問,多Launcher選擇介面時也因沒進入Launcher而不發ACTION_BOOT_COMPLETED廣播而造成待不了機。
當ActivityManagerService跑起來就表示mSystemReady 為true,可以通過查看logcat,如果看到有“Activity Manager”的log表示已跑ActivityManagerService,如果看到“** Failure starting bootstrap service”,表示啟動失敗。
待機異常2:可以進early_suspend,但進不了suspend
問題分析
進入suspend時,會調用pm_get_wakeup_count,如果系統存在wake_lock就會卡在這個函數裡,如:
pm_get_wakeup_count:
prepare_to_wait(&wakeup_count_wait_queue, &wait,
TASK_INTERRUPTIBLE);
當wake_lock釋放完畢時才會喚醒該wait,如下inpr = 0:
wakeup_source_deactivate:
if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
wake_up(&wakeup_count_wait_queue);
調試方法
在串口終端下,不要串連USB,連USB會產生wake_lock,cat /sys/power/wakeup_count,這時會去調用pm_get_wakeup_count,如果cat也卡住,說明存在suspend確實是卡在wake_lock上。
Wake_lock有些是android進程加的和有些是kernel進程加的。可以通過命令cat /sys/kernel/debug/wakeup_sources 來查看所有的wake_lock,如下:
active_since 所在列,如果有數值大於0,表示其所對應的行是當前活動的wake_lock,如,PowerManagerService.WakeLocks和dwc_wake_lock是活動的。
原代碼的列印有可能對不齊,下面的改動可以讓其對齊:
--- a/kernel/drivers/base/power/wakeup.c+++ b/kernel/drivers/base/power/wakeup.c@@ -842,8 +842,7 @@ static int print_wakeup_source_stats(struct seq_file *m,- ret = seq_printf(m, "%-12s\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",+ ret = seq_printf(m, "%-35s%-15lu%-15lu%-15lu%-15lu%-15lld%-15lld%-15lld%-15lld%-15lld\n", ws->name, active_count, ws->event_count, ws->wakeup_count, ws->expire_count, ktime_to_ms(active_time), ktime_to_ms(total_time),@@ -863,9 +862,8 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) {- seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"- "expire_count\tactive_since\ttotal_time\tmax_time\t"- "last_change\tprevent_suspend_time\n");+ seq_printf(m, "%-35s%-15s%-15s%-15s%-15s%-15s%-15s%-15s%-15s%-15s\n",+ "name","active_count","event_count","wakeup_count","expire_count","active_since","total_time","max_time","last_chang
android加的wake_lock可以通過命令cat /sys/power/wake_lock來查看,再在kernel/kernel/power/main.c 的wake_lock_store函數裡加上列印: printk(“pid %d wirte %s to /sys/power/wake_lock\n”,current->pid,buf);,將pid列印出來,然後cat /proc/進程ID/status 來查看具體進程(也可以 ps 進程ID號來查看)。
待機異常3: 可以進suspend,但出現:PM: Some devices failed to suspend
問題分析
有些裝置在待機時失敗了。
調試方法
首先用命令cat /sys/kernel/debug/suspend_stats查看待機異常發生在哪個階段,如下:
success: 3
fail: 1
failed_freeze: 0
failed_prepare: 0
failed_suspend: 1
failed_suspend_late: 0
failed_suspend_noirq: 0
failed_resume: 0
failed_resume_early: 0
failed_resume_noirq: 0
failures:
last_failed_dev:
last_failed_errno: -16
0
last_failed_step: suspend
failed_suspend 的記數大於0表示在device_suspend階段異常。
從log可以看到,如果是這樣的欄位:
active wakeup source: event3-556
PM: Some devices failed to suspend
則表示有些活動wakeup鎖還沒釋放。在kernel/drivers/base/power/wakeup.c的wakeup_source_add函數裡添加列印:
printk(“%s:%s\n”,FUNCTION,ws->name);
dump_stack();
跟蹤出哪個驅動,然後在該驅動裡跟蹤wakeup鎖。
如果log是這樣的:
PM: Device xxx failed to xxx: error x
PM: Some devices failed to suspend
則表示有裝置在待機時失敗,根據裝置名稱找到相應驅動,然後跟蹤該驅動suspend失敗的原因。
另外,將kernel/kernle/printk.c裡的ignore_loglevel置1:
Kernel/kernel/printk.c
static bool __read_mostly ignore_loglevel=1;
用命令echo 1 > /sys/power/pm_print_times 開啟kernel/drivers/base/power/main.c裡的initcall_debug_report列印,這樣,在待機時就可以列印出哪些裝置待機成功或失敗。
待機異常4: 可以進入到suspend_enter,suspend流程走完了,但很快被喚醒
問題分析
Suspend流程走完後被喚醒,說明有喚醒源異常,造成異常喚醒。
調試方法
首先用命令cat /sys/kernel/debug/suspend_stats查看待機過程是否正常,如果正常,那麼需要找出喚醒源。
開啟kernel/kernel/irq/pm.c的resume_irqs函數裡的列印資訊:
#ifdef CONFIG_PM_DEBUG if (desc->istate & IRQS_PENDING) { printk(KERN_DEBUG "Wakeup from IRQ %d %s\n", irq, desc->action && desc->action->name ? desc->action->name : ""); }#endif /* CONFIG_PM_DEBUG */
從列印資訊裡可以看出是哪個中斷號喚醒的,命令cat /proc/interrupts可以查看所有中斷。另外,在kernel/kernel/irq/manage.c 的__setup_irq函數裡加列印及dump_stack,跟蹤哪個驅動註冊了該irq號:
printk("%s:irq = %d,name = %s\n",__FUNCTION__,irq,new->name ? new->name : "");dump_stack();
待機異常5: 可以進入到suspend_enter,也不被喚醒,但電流很大,CPU也較燙
問題分析
進入suspend_enter且不被喚醒,說明系統已進入待機,應該是SOC沒進入待機狀態造成
調試方法
可以在suspend_enter加SOC power state的寄存器的列印,或者跟vendor要soc待機的調試手段,比如 intel平台,可以通過查詢cat /sys/kernel/debug/mid_pmu_states 查詢cpu有幾次進入待機狀態。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
待機異常篇