开机时间优化总结

开机时间:从按下Power键到进入Launcher界面这个过程所花费的时间。
开机顺序:(preloader ->) lk -> kernel -> init -> Zygote -> SystemServer -> PMS -> Launcher。
关于每个阶段的启动细节请参阅前面的文章,本文将重点介绍开机时间优化的策略。

一、分析工具

除了uart 、 logcat日志,Android提供了bootchart

1.1 bootchart

bootchart可为整个系统提供所有进程的CPU和I/O负载细分。要启动bootchart需要执行指令:

1
2
adb shell 'touch /data/bootchart/enabled'
adb reboot

重启后执行源码system/core/init/目录的grab-bootchart.sh脚本。

此外,还可以用系统脚本来对比两次的bootchart中不断阶段启动时间的差异

1
system/core/init/compare-bootcharts.py bootchart_1_dir bootchart_2_dir

1.2 systrace

systrace允许在启动期间收集kernel和Android的trace记录。systrace的可视化可以帮助分析启动过程中的具体问题。不过,要查看整个启动过程中的平均数量或累计数量,直接查看kernel trace记录更为方便)。

启用systrace的方法:

(1)在frameworks/native/atrace/atrace.rc中,将”write /sys/kernel/debug/tracing/tracing_on 0”注释掉。

1
#write /sys/kernel/debug/tracing/tracing_on 0

(2)在device/$project/device.mk文件中添加:

1
2
PRODUCT_PROPERTY_OVERRIDES +=  debug.atrace.tags.enableflags=802922
PRODUCT_PROPERTY_OVERRIDES += persist.traced.enable=0

(3)在device/$project/BoardConfig.mk文件中添加:

1
BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug

(4)在device/$project/init.target.rc文件中添加:

1
2
3
4
5
on property:sys.boot_completed=1
write /d/tracing/tracing_on 0
write /d/tracing/events/ext4/enable 0
write /d/tracing/events/f2fs/enable 0
write /d/tracing/events/block/enable 0

启动手机后先通过adb从手机中取出systrace:

1
2
3
adb root
adb shell atrace --async_stop -z -c -o /data/local/tmp/boot_trace
adb pull /data/local/tmp/boot_trace

然后加上’–from-file=boot_trace’参数执行源码external/chromium-trace目录的systrace.py脚本。

二、优化策略

2.1 bootloader

1.关闭uart日志或减少不必要的日志输出。

a. 从kernel defconfig中移除 “CONFIG_SERIAL_MSM_HSL_CONSOLE=y”。
b. 将device/qcom/msm8996/BoardConfig.mk定义的BOARD_KERNEL_CMDLINE中 “earlyprintk=msm_hsl_uart,0xf991e000” 信息移除。

2.检查CPU时钟并考虑kernel加载和初始化I/O并行进行。

3.将在bootloader中花费的启动时间以命令行的形式传递到kernel。

2.2 kernel

查看bootprof或bootchart,如包括kernel在内每个启动阶段耗时都比较长,则需要检查kernel配置。

1.减少kernel defconfig

减少kernel中配置可以减小kernel大小,加快加载、解压缩。一般可以从两个方面出发:
(1) 查看/dev、 /sys目录,将未使用的driver相关selinux节点标签一并移除。
(2) 取消不使用的CONFIG。
(3) 对比userdebug和user的配置,移除debug/trace相关的的CONFIG。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
-CONFIG_CGROUP_DEBUG=y
-CONFIG_MSM_ADSPRPC=y
-CONFIG_REGULATOR_TPS65132=y
-CONFIG_PFT=y
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_EVENT=y
-CONFIG_CORESIGHT_FUSE=y
-CONFIG_CORESIGHT_CTI=y
-CONFIG_CORESIGHT_TMC=y
-CONFIG_CORESIGHT_TPIU=y
-CONFIG_CORESIGHT_FUNNEL=y
-CONFIG_CORESIGHT_REPLICATOR=y
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_HWEVENT=y
-CONFIG_CORESIGHT_ETMV4=y
-CONFIG_CORESIGHT_MODEM_ETM=y
-CONFIG_CORESIGHT_WCN_ETM=y
-CONFIG_CORESIGHT_RPM_ETM=y
-CONFIG_CP_ACCESS64=y
-CONFIG_MSM_SMD_DEBUG=y
-CONFIG_MSM_DEBUG_LAR_UNLOCK=y
-CONFIG_MSM_FORCE_WDOG_BITE_ON_PANIC=y
-CONFIG_MSM_OCMEM_DEBUG=y
-CONFIG_MSM_OCMEM_NONSECURE=y
-CONFIG_LOCKUP_DETECTOR=y
-CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
-CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_ATOMIC_SLEEP=y
-CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_DEBUG_MEMORY_INIT=y
-CONFIG_DEBUG_LIST=y
-CONFIG_FAULT_INJECTION=y
-CONFIG_FAIL_PAGE_ALLOC=y
-CONFIG_FAULT_INJECTION_DEBUG_FS=y
-CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
-CONFIG_MSM_RTB=y
-CONFIG_MSM_RTB_SEPARATE_CPUS=y
-CONFIG_DYNAMIC_DEBUG=y
-CONFIG_PANIC_ON_DATA_CORRUPTION=y
-CONFIG_STRICT_MEMORY_RWX=y
-CONFIG_CMA_SIZE_MBYTES=40
-CONFIG_MSM_CAMERA_DEBUG=y
-CONFIG_MSMB_CAMERA_DEBUG=y
-CONFIG_MSM_BOOT_STATS=y
-CONFIG_MSM_COMMON_LOG=y
-CONFIG_MSM_CORE_HANG_DETECT=y
-CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
-CONFIG_MSM_RPM_LOG=y
-CONFIG_MSM_RPM_STATS_LOG=y
-CONFIG_MSM_SMEM_LOGGING=y
-CONFIG_MSM_DCC=y
-CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
-CONFIG_MSM_GLINK_PKT=y
-CONFIG_TRACER_PKT=y
-CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
-CONFIG_IOMMU_TESTS=y
-CONFIG_MSM_TZ_LOG=y
-CONFIG_LOG_BUF_MAGIC=y
-CONFIG_DEBUG_INFO=y
-CONFIG_PAGE_OWNER=y
-CONFIG_SLUB_DEBUG_PANIC_ON=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
-CONFIG_SLUB_DEBUG_ON=y
-CONFIG_DEBUG_KMEMLEAK=y
-CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
-CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
-CONFIG_PANIC_ON_SCHED_BUG=y
-CONFIG_PANIC_ON_RT_THROTTLING=y
-CONFIG_UFS_FAULT_INJECTION=y
-CONFIG_BLK_DEV_IO_TRACE=y
-CONFIG_CPU_FREQ_SWITCH_PROFILER=y
-CONFIG_FREE_PAGES_RDONLY=y
-CONFIG_KERNEL_TEXT_RDONLY=y

移除其它平台配置
-CONFIG_ARCH_MSM8917=y
-CONFIG_ARCH_MSM8920=y
-CONFIG_ARCH_MSM8940=y
-CONFIG_ARCH_MSM8953=y

CONFIG_TASKSTATS
CONFIG_TASK_DELAY_ACCT
CONFIG_TASK_XACCT
CONFIG_TASK_IO_ACCOUNTING
CONFIG_CLEANCACHE
CONFIG_CMA_DEBUGFS
CONFIG_PM_DEBUG
CONFIG_L2TP_DEBUGFS
CONFIG_SERIAL_MSM_GENI_CONSOLE
CONFIG_UID_SYS_STATS
CONFIG_QCOM_GPI_DMA_DEBUG
CONFIG_MSM_GLADIATOR_HANG_DETECT
CONFIG_ICNSS_DEBUG
CONFIG_PAGE_OWNER_ENABLE_DEFAULT
CONFIG_DEBUG_PAGEALLOC
CONFIG_SLUB_DEBUG
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT
CONFIG_PAGE_POISONING
CONFIG_WQ_WATCHDOG
CONFIG_SCHED_STACK_END_CHECK
CONFIG_QCOM_RTB
CONFIG_QCOM_RTB_SEPARATE_CPUS
CONFIG_FUNCTION_TRACER
CONFIG_IRQSOFF_TRACER
CONFIG_PREEMPT_TRACER
CONFIG_MEMTEST
CONFIG_FB_MSM_MDSS_XLOG_DEBUG
CONFIG_MSM_SMD_DEBUG
CONFIG_SCHED_DEBUG
CONFIG_DEBUG_BUS_VOTER
CONFIG_HAVE_DEBUG_KMEMLEAK
CONFIG_HAVE_DEBUG_BUGVERBOSE
CONFIG_QCOM_COMMON_LOG
CONFIG_QCOM_CPUSS_DUMP

2.延迟初始化

很多进程都在设备启动期间启动,在kernel启动期间执行initcall来识别启动速度缓慢且对启动init进程不重要的外设/组件,然后通过将这些外设/组件移入可加载的内核模块将其延迟到启动过程的后期来启动。

在kernel/init/main.c中将”initcall_debug”设为”1”后kernel启动会打印下列信息:

1
initcall <driver_init_function> returned 0 after <time in microsecond>"

2.3 init

1.调整filesystem

当从头开始读取某个文件或依序读取块时,预读的 Linux 内核便会启动,这就需要调整专门用于启动的I/O调度程序参数(与普通应用的工作负载特性不同)。此外,因为分区的客制化,可能原生的部分分区被移除,需要检查rc文件将mount相关代码移除,避免等待mount耗时过久 。

2.优化rc文件

(1)识别init中的缓慢操作:Android 8.0通过新的wait_for_property命令支持此。系统会记录init命令 exec/wait_for_property或任何所需时间较长的操作(指所需时间超过 50 毫秒的任何命令)。例如:

1
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

(2)启动服务并及早启用关键路径中的外围设备。例如,有些SOC需要先启动安全相关服务,然后再启动SurfaceFlinger。在ServiceManager返回“wait for service”(等待服务)时查看系统日志 - 这通常表明必须先启动依赖服务。

(3)移除 init.*.rc 中所有未使用的服务和命令。只要是早期阶段的 init 中没有使用的服务和命令,都应推迟到启动完成后再使用。

3.优化I/O

有时检查flash性能,其质量不好也会影响performance。检查UFS信息:

1
2
adb shell cat /sys/block/sda/device/vendor SAMSUNG
adb shell cat /sys/block/sda/device/model KLUCG4J1ED-B0C1

系统中自带用来分析trace的脚本:

system/extras/boottime_tools/bootanalyze/bootanalyze.py:负责衡量启动时间,并详细分析启动过程中的重要步骤。
system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace:提供每个文件的访问信息。
system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace:提供系统级细分信息。

2.4 Zygote

采用文件级加密的设备可以在zygote-start触发器的早期阶段启动zygote(默认情况下,zygote会在main中启动,比zygote-start晚得多)。
可用下面指令检查FBE/FDE状态:

1
2
3
adb shell getprop | grep crypt
#FDE:ro.crypto.type=block, ro.crypto.state=encrypted
#FBE: ro.crypto.type=file, ro.crypto.state=encrypted

常见耗时点:

(1)ZygoteInit.java中预加载class/resource的时间,需确认是否新添加很多系统资源,preloaded classes在framework/base/preloaded-classes中定义,需适当减少不需要的预加载类与资源。

(2)查看这期间是否有很多GC动作。

2.5 SystemServer

检查系统service开机时间初始化过程的耗时情况。常见耗时点

(1)pms scan package阶段
通常原因:预置apk过多,有的apk太大扫描时间过长。建议减少elapsedtime > 100ms的apk。

1
2
3
4
5
6
7
8
9
10
build/target/product/base.mk build/target/product/core.mk
build/target/product/core_base.mk
build/target/product/core_minimal.mk
build/target/product/full_base.mk
build/target/product/full_base_telephony.mk
build/target/product/generic_no_telephony.mk
device/qcom/common/base.mk
vendor/qcom/proprietary/common/config/device-vendor.mk
vendor/qcom/proprietary/common/msm8996/BoardConfigVendor.mk
vendor/qcom/proprietary/qrdplus/Extension/products.mk

(2)pms dexopt阶段
目前有些apk如facebook,wechat等,本身应用较大且code复查度高,往往会出现安装慢等问题,主要由于dex2oat做得comiler久的原因。
修改方法是将这些apk加到whitelist中,编译的时候让其做得简单些:

1
2
3
4
if ((oat_filename_find("facebook") != std::string::npos) || (oat_filename_find("google") != std::string::npos)) {
compiler_filter_string = "interpre-only";
LOG(INFO) << "This apk is in whitelist from property so set interpret-only";
}

(3)其他service初始化过程耗时,需要具体分析相应service。
在init.rc里以及其他平台init.plat**.rc里把不需要的service都注释掉不加载。

2.6 Bootanimation

控制动画包中资源数量及大小,另外考虑缩短循环播放和结尾的时间。

调试手段:
禁用开机动画:将”debug.sf.nobootanimation”设为”1” device/qcom/msm8996/system.prop
禁用锁屏:将”def_lockscreen_disabled”设为”true” frameworks/base/packages/SettingsProvider/res/values/defaults.xml

2.7 ODEX

1.打开预编译提取odex的方式

在device/$project/BoardConfig.mk中定义:

1
2
3
WITH_DEXPREOPT := true
WITH_DEXPREOPT_PIC := true
#DONT_DEXPREOPT_PREBUILTS := true

2.部分APK跳过odex提取

在Android.mk中添加”LOCAL_DEX_PREOPT := false”

2.8 CPU

1.在开机用adb指令查看CPU的运行频率及online的CPU

1
2
adb shell cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
cat /sys/devices/system/cpu/cpu1/online

2.提升CPU频率 - 将所有CPU切换至性能模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
adb shell stop perf-hal-1-0

adb shell "echo 1 > /sys/devices/system/cpu/cpu0/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu1/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu2/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu3/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu4/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu5/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu6/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu7/online"

adb shell "echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu3/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu4/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu5/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu6/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu7/cpufreq/scaling_governor"

adb shell "echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom,mincpubw/governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom,memlat-cpu0/governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom, memlat-cpu6/governor"

3.提升GPU频率

1
2
3
adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"

4.提升bus频率
adb shell “echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor“
adb shell “echo performance > /sys/class/devfreq/soc:qcom,mincpubw/governor”
adb shell “echo performance > /sys/class/devfreq/soc:qcom,memlat-cpu0/governor”
adb shell “echo performance > /sys/class/devfreq/soc:qcom, memlat-cpu4/governor”

5.隔离CPU 通过sysfs修改max_cpus/min_cpus:
(1)小集群

1
2
/sys/devices/system/cpu/cpu0/core_ctl/max_cpus
/sys/devices/system/cpu/cpu0/core_ctl/min_cpus

(2)大集群

1
2
/sys/devices/system/cpu/cpu4/core_ctl/max_cpus
/sys/devices/system/cpu/cpu4/core_ctl/min_cpus

(3)调参数

1
2
3
4
MPCTLV3_MIN_ONLINE_CPU_CLUSTER_BIG = 0x41000000
MPCTLV3_MIN_ONLINE_CPU_CLUSTER_LITTLE = 0x41000100
MPCTLV3_MAX_ONLINE_CPU_CLUSTER_BIG = 0x41004000
MPCTLV3_MAX_ONLINE_CPU_CLUSTER_LITTLE = 0x41004100

独立的核: /sys/devices/system/cpu/cpuX/isolate