Android手机功耗

极力推荐Android 开发大总结文章:欢迎收藏
程序员Android 力荐 ,Android 开发者需要的必备技能

本篇文章主要介绍手机开发中的功耗部分知识点,功耗直接影响到手机的待机时间,通过阅读本篇文章,您将收获以下内容:

一、手机功耗问题浅析博文
二、Sleep 、suspend
三、SPM (System Power Manager)
四、Deep idle
五、SODI (screen on deep idle)
六、systrace/ftrace
七、wireshark
八、layerdump
九、如何确定阻止进入suspend的原因
十、如何分析wakelock(wakeup source)持锁问题
十一、如何看SPM的状态是否正确
十二、如何查找待机唤醒源
十三、如何找到阻止进入deep idle / SODI的元凶

一、手机功耗问题浅析博文

手机功耗问题直接影响到手机的待机时长,因此,解决功耗篇问题对于智能机十分必要。
之前有写过一个功耗浅析的文章,可以先参考下:
手机功耗问题浅析博文

二、Sleep 、suspend

这里的suspend确切的说是MCU(ARM )suspend,也就是cpu进入Wait for interrupt 状态(WFI);因为对整个系统来说,CPUWFI是整个系统睡眠的先决条件,我们debug也是从CPU是否进入WFI开始,从Linux的角度来说,CPU进入suspend就是SW完全不跑了,停在suspend workqueue里面。

从灭屏到CPU进入suspend的大体流程框架如下:

从灭屏到进入suspend的大体流程框架

相关code路径:

/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

/frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

/system/core/libsuspend/

/kernel-x.x/kernel/power/

三、SPM (System Power Manager)

因为整个系统不只是AP(MCU),还包括modem、connectivity等子系统;
CPU 进入 WFI 后,整个系统就依靠一颗 SCP:SPM 来控制睡眠/唤醒的流程,它会去关注各个子系统的状态

SPM =System Power Manager
它掌控着cpu suspend之后系统是否能掉到最小电流的关键逻辑,你可以把它理解成一个投票机制,当系统的关键资源(memory、clock)没有任何人使用的时候,它就会让系统进入一个真正的深睡状态(最小电流)只要它检测到有任何资源请求还没释放,系统就无法降到底电

所以在底电问题上的debug流程中,我们不仅仅要看cpu有没有suspend成功,还要看SPM的状态是否正确,SPM里面有一个可编程控制器PCM(Programmable Command Master)CPU在进去WFI之前会把SPMfirmware写入PCM,然后PCM就依据firmware的逻辑来控制SPM的工作

SPM强相关的一个东西就是系统中的时钟请求信号,也就是26M时钟开关的控制逻辑;因为系统工作在最小电流的时候,SPM只依靠32K时钟工作;因此要判断系统是不是已经到深睡状态,就要看26M有没有关闭

26M时钟的控制逻辑概要如下图

26M 时钟控制逻辑

所以从上图我们就可以看到, 26M有没有关,就只要看SCLKENA这个信号有没有关闭;而SPM对这个信号的输出以及子系统的信号输入,都会记录在SPM的寄存器里面,这个就是我们通过log排查的依据

代码路径
/kernel-x.x/drivers/misc/mediatek/base/power/spm_vx/

四、Deep idle

Deep idle 基本概念

首先顾名思义,这是一种CPU进入空闲后的状态,也就是在idle进程中执行的

简单地说,Mediatek会在CPU进入空闲的情况下,再去关闭一些不必要的power domain,以达到最省电的目的,因为CPU空闲的时候,其实系统中有不少的domain也是不需要运行的,不这样做的话,就仅仅是CPU这块的电省下来 ,达不到省电的目的。

Mediatek的做法是在CPU在进入idle进程后,会去判断当前系统的状态是否满足进入更省电状态的条件,首先就会检查是否能进入deep idle,因为dpidle最省电

系统进入dpidle需要满足的条件是

  • 单核(BY_CPU)
  • 预设的能block deep idle的所有clock都已经关闭(BY_CLOCK)
  • CPU在2ms内没有从idle task调度出去的需求(BY_TMR)
  • BY_VTG / BY_OTH的case很少(BY_OTH在个别平台跟TEE(SPI指纹模块)有关)

我们可以从波形上检查系统是否进入deep idle

下图中电流的底部就是deep idle的状态,在MP3播放的状态大约20mA
如果没有进deep idle,这个底部会被抬高

deep idle的状态

deep idle也是由SPM来控制它的执行逻辑,跟suspend一样, CPU在进去WFI之前会把SPMfirmware写入PCM,这个firmwaresuspend是完全不一样的。

五、SODI (screen on deep idle)

SODI:Screen On Deep Idle
SODIdeep idle类似,是SPM的另外一种工作模式.SODI的进入条件跟 deep idle是类似的,区别只是要检查的clockdeep idle不完全一样 ,SODIdisplay功耗的影响相对于CMD / VDO mode是不一样的

前面讲过了CMD / VDO的差别,其实就很容易理解这一点:因为CMD mode下,CPU不用送数据出去,因此MIPI clock可以不用送,这整条clock路径上的东西(PLL/clock)都可以关闭,而且memory跟VDO相比也可以做更多省电的action;所以SODICMD mode的省电效果会比VDO的效果更明显,是否进入SODI也可以从波形上明显地看到:

下图示SODI enable/disableidle mode波形比较

CMD mode:SODI on(左) vs SODI off(右)

CMD mode

VDO mode:SODI on(左) vs SODI off(右)

VDO mode

重点关注波形的形状,电流下降的数值不同平台不一样

六、systrace/ftrace

systrace/ftrace 也是我们分析功耗问题常用的工具,systrace/ftrace可以帮你定位到是谁在使用CPU,也可以用来分析idle状态下的毛刺波形是谁触发的,来定位root cause

七、wireshark

为什么要使用wireshark
wireshark是我们用来分析netlog的一个工具.通常用来定位开数据连接的待机功耗问题,查找是哪个APP/Process在使用数据.wireshark可以在公共网络上下载到,一般公司负责协议/TCP/Wifi这些部门也会有这个工具

怎么使用wireshark
首先需要在抓log时,打开mtklog中的netlog,就可以找到netlog对应的文件
 netlog

wireshark打开这个.cap文件,界面如下

用wireshark打开这个.cap文件

有时候最前面的【时间戳】格式会不对,会跟mobile log对不上,如果遇到了,可以通过如下菜单调整[View]->[Time Display Format]

时间戳格式

八、layerdump

查看图层的 adb 命令:
adb shell dumpsys SurfaceFlinger

下面是launcher界面的图层:

launcher的图层

九、如何确定阻止进入suspend的原因

系统没有进入suspend,主要的原因是 因为系统有锁导致.

锁一般分为:

  • APP通过PowerManager拿锁,
  • 以及kernel wakelock.

分析上层持锁的问题:
目前PowerManagerService的log 默认不会打开,可以通过修改:
/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private static final boolean DEBUG = true;
    private static final boolean DEBUG_SPEW = DEBUG && false;
修改为:
    private static final boolean DEBUG = true;
    private static final boolean DEBUG_SPEW = true;
打开上层的log

通过 syslog:搜索关键字:total_time= 来确定持锁的时间.

PowerManagerService: releaseWakeLockInternal: lock=31602562 
[job/DownloadManager:com.android.providers.downloads], flags=0x0, 
total_time=600051ms

十、如何分析wakelock(wakeup source)持锁问题

kernel的锁默认不会打印出来,一般是待机结束后通过节点来获取:
adb shell cat /sys/kernel/debug/wakeup_sources > wakeup_sources.log

名称 解释
active_count: 对应wakeup source被激活的次数.
event_count: 被信号唤醒的次数
wakeup_count: 中止suspend的次数.
expire_count: 对应wakeup source超时的次数.
active_since: 上一次还活跃的时间点.时间单位跟kernel log前缀时间是一样(kernel单调递增时间).
total_time: 对应wakeup source活跃的总时长.
max_time: 对应的wakeup source持续活跃最长的一次时间.
last_change: 上一次wakeup source变化的时间(从持锁到释放or释放到持锁),时间单位跟kernel log前缀时间是一样(kernel单调递增时间).
prevent_suspend_time: 对应wakeup source阻止进入autosleep的总累加时间.

一般情况下:
如果是复现机,前面没有捉log,也没有dump log,只有一份wakeup_sources.log
可以看下prevent_suspend_time,一般时间越大越可能是阻止系统进入suspendwakeup sources.

如果测试前后,都有捉 wakeup_sources.log 请对比两份wakeup_sources.logtotal time的差值.
差值时间跟灭屏的时间对得上,一般就是这个锁引起的问题.

比两份的的差值

把捉出来的wakeup_sources.log复制到excel表格中,比较好对齐,一个是比较好计算.

image.png

其中dispsys_wakelock, total_time的时间有697614mS 也就是总共有697s.

image.png

或者在待机测试结束后通过命令:
adb bugreport > bugreport.txt

底层的锁:

All kernel wake locks: 
Kernel Wake lock ttyC0 : 1h 33m 15s 668ms (3856 times) realtime 
Kernel Wake lock radio-interface: 1h 20m 56s 210ms (3995 times) realtime 
Kernel Wake lock ccci3_at : 1h 9m 43s 491ms (2932 times) realtime 
Kernel Wake lock ccci_fs : 1h 0m 52s 818ms (3432 times) realtime 
Kernel Wake lock ccci3_at2 : 41m 16s 938ms (2465 times) realtime

上层的锁:

All partial wake locks: 
Wake lock 1001 RILJ: 5m 29s 768ms (13118 times) realtime 
Wake lock 1000 *alarm*: 4m 7s 823ms (2330 times) realtime 
Wake lock 1000 ConnectivityService: 59s 513ms (1 times) realtime 
Wake lock u0a111 *alarm*: 50s 334ms (751 times) realtime 
Wake lock u0a111 WakerLock:999603354: 28s 655ms (125 times) realtime 
Wake lock 1000 NetworkStats: 11s 434ms (569 times) realtime

十一、如何看SPM的状态是否正确

待机被唤醒之后,确认:大部分debug_flag最后的bit位是不是 f or ff, 说明系统还有模块咬住26M,系统并没有最后进入真正的suspend.

譬如下面的log 最后bit 位是 0 都是有问题的:

<4>[ 250.874040] -(0)[1244:system_server][SPM] wake up byCONN2AP, timer_out = 8635, r13 = 0x20045038, debug_flag = 0x9 0

<4>[ 600.704307] -(0)[2722:system_server][SPM] wake up by R12_EINT_EVENT_B, timer_out = 81779, r13 = 0xe040000, debug_flag = 0x113f 0

十二、如何查找待机唤醒源

系统场景的唤醒源如下:

  • EINT
  • CONN
  • CLDMA

EINT:

PMIC的唤醒.

  • a.Powerkey
    唤醒后面的log会有 pwrkey_int_handler

  • b. rtc alarm
    唤醒后面的log会有 alarm time is up

 rtc alarm

具体类型的唤醒包,可以确认:
syslog里面搜索关键字 wakeup alarm:

01-25 01:23:04.026 830 898 D AlarmManager: wakeup alarm = Alarm{3e671462 type 2 when 27213196 com.android.phone}; package = com.android.phoneneedGrouping = true

一般alarm的唤醒,除了第三方APK之外,有时会遇到类似android/phone APK的唤醒.
确认具体android的唤醒的原因,需要确定唤醒后,紧接着发下来的intent事件.

01-24 19:50:30.031 830 898 D AlarmManager: wakeup alarm = Alarm{9ba1b41 type 2 when 7259546 android}; package = androidneedGrouping = false 01-24 19:50:30.031 830 898 V ActivityManager: Broadcast: Intent { act=android.content.syncmanager.SYNC_ALARM· flg=0x114 (has extras) } ordered=true userid=0 callerApp=null·

搜索:android.content.syncmanager.SYNC_ALARM ,可以定位到:/frameworks/base/services/core/java/com/android/server/content/SyncManager.java
进一步找对应owner确认.

phone apk的唤醒:

数据网络的定时恢复.

  • c. others

kernel log有关键字:EINT.*is pending
序号:206,EINT 206 is pending,需要结合DCT跟cat /proc/interrupts

1.通过DCT:

通过DCT

2.cat /proc/interrupts :pmic-eint对应的序号是150.

289: 149 mt-eint 1 TOUCH_PANEL-eint
291: 0 mt-eint 3 11240000.msdc1 cd
294: 0 mt-eint 6 ALS-eint
295: 0 mt-eint 7 mrdump_ext_rst-eint
314: 73 mt-eint 26 irq_nfc-eint
332: 246 mt-eint 44
432: 0 mt-eint 144 iddig_eint
438: 341 mt-eint **150** pmic-eint
440: 0 mt-eint 152 spm_vcorefs_start_eint
441: 0 mt-eint 153 spm_vcorefs_end_eint
442: 0 mt-eint 154 spm_vcorefs_err_eint

<5>[30640.939329] -(0)[1191:system_server]EINT **150** is pending
......
<3>[30640.942131] (0)[69:pmic_thread]kpd: Power Key generate, pressed=1
<3>[30640.942189] (0)[69:pmic_thread]kpd: kpd: (pressed) HW keycode =116 using PMIC

CLDMA:
确认唤醒的channel ID关键字CLDMA_MD, wakeup source
CLDMA 唤醒源确认

常用的唤醒的channel:

[channel 10]
channel 10

常见的AT command的唤醒:参考下面【常见AT 命令解析】

命令 解释
AT +ECOPS PLMN信息变化.
搜网的次数 AT: CREG   CGREG   (一个是CS,一个是PS)
网络PDP状态变化的次数 AT:CGEV  (PDN activate/deactivate)
VOLTE功能导致唤醒的次数 AT: CIREPI   CIREPH CNEMS1  CIREG EIMS
LTE数据连接 AT: EDRBSTATE

[channel 14]
一般是跟channel10一起产生,modem小区消息变化,会记录小区的信息到nvram.

[channel20/24]
数据连接的唤醒.

channel20/24

使用winshark打开netlog:

winshark

main log里面搜索IP 地址:

112.17.251.148
01-06 21:47:58.060   313 25080 D libc-netbsd: res_queryN name =** push.hexin.cn **succeed
01-06 21:47:58.060 25056 25081 I AppStore.StorageManager: ab:e:150407-372=>in removeSpuriousFiles
01-06 21:47:58.060 21561 25079 D libc-netbsd: getaddrinfo: push.hexin.cn get result from proxy >>
01-06 21:47:58.060 21561 25079 I System.out: [propertyValue:true](http://propertyvaluetrue/)
01-06 21:47:58.061 21561 25079 I System.out: [CDS]connect[[**push.hexin.cn**/112.17.251.148:8887](http://push.hexin.cn/112.17.251.148:8887)] tm:90

[channel32]
modem SIM driver获取sim GPIO状态所造成的唤醒. 这部分情况比较少,确认review 贵司sim driver是否有相关的design.

[channel34]
WIFI4G 有部分频段是重叠的,modem 需要把频段的信息通知WIFI所造成的唤醒. 这部分属于正常的design.

[channel55]
VOLTE 网络的唤醒.

[channel6/42]
这是打开modem log造成的,分析功耗问题除非发现跟modem有关系,否则捉log时,不要打开modem log

具体的channel 对应的定义可以参考:

N版本:

  N版本:
 /kernel-4.4/drivers/misc/mediatek/eccci/ccci_core.h 

  M版本:
 /kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt_ccci_common.h 

  L版本:
 /kernel-3.10/include/mach/mt_ccci_common.h

L版本:
/kernel-3.10/include/mach/mt_ccci_common.h

[channel 14与10]常见AT 命令解析,注意红色字体部分字段

网络切换状态AT:

命令1:AT+EDRBSTATE

命令2:AT< +COPS(运营商信息)

命令4: AT< +CGREG

命令6:AT+EGTYPE(attach状态)

命令8:AT+EI3GPPIRAT(C2K切网)

信号强度相关AT:

命令7:AT< +ECSQ(信号强度)

网络注册状态AT:

命令3:AT< +CREG(NEWORK REGISTER)

命令4: AT< +CGREG

网络VOLTE支持情况上报:

命令5:AT+CIREP (IMS网络支持情况)

十三、如何找到阻止进入deep idle / SODI的元凶

image.png

如果是由于CLOCK 卡住,请参考下面的flow:

Debug节点:/sys/kernel/debug/cpuidle/

-rw-r--r-- 1 root root 0 1970-01-01 00:00 dpidle_state
-rw-r--r-- 1 root root 0 1970-01-01 00:00 idle_state
-rw-r--r-- 1 root root 0 1970-01-01 00:00 mcidle_state
-rw-r--r-- 1 root root 0 1970-01-01 00:00 reg_dump
-rw-r--r-- 1 root root 0 1970-01-01 00:00 slidle_state
-rw-r--r-- 1 root root 0 1970-01-01 00:00 soidle3_state
-rw-r--r-- 1 root root 0 1970-01-01 00:00 soidle_state

从节点中确认:/sys/kernel/debug/cpuidle/dpidle_state

其中dpidle_block_mask 里面的数值对应的bit位为1的,代表对应的clock卡住系统进入省电idle了.
从上图看:

INFRA 的CG group占用的clock是从bit 0到bit31

PERI 的CG group 占用的clock是从bit32 到bit63

DISP0的CG group 占用的clock是从bit64到bit95

以此类推

N版本对应平台的clock ID:

6735/6737:
kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6735/include/mach/mt_clkmgr1_legacy.h
6735M:
kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6735/include/mach/mt_clkmgr2.h
6753:
kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6735/include/mach/mt_clkmgr3.h

cg_clk_id

enum cg_clk_id {
MT_CG_INFRA_DBGCLK = 0,
MT_CG_INFRA_GCE = 1,
MT_CG_INFRA_TRBG = 2,
MT_CG_INFRA_CPUM = 3,
MT_CG_INFRA_DEVAPC = 4,
MT_CG_INFRA_AUDIO = 5,
MT_CG_INFRA_GCPU = 6,
MT_CG_INFRA_L2C_SRAM = 7,
MT_CG_INFRA_M4U = 8,
MT_CG_INFRA_CLDMA = 12,

至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

微信关注公众号:  程序员Android,领福利

posted @ 2019-08-09 14:32  程序员Android  阅读(2236)  评论(0编辑  收藏  举报