本文针对Android系统Bootchart部分源码进行了分析,并给出了Android-5、6-7、8+版本的参数差异,解释了8+以上的时长无法手动控制的原因,并对bootchart源码进行了分析
目录
- 研究背景
- 采集时长控制
- Android 5
- Android 6-7
- Android 8+
- Bootchart源代码分析
- 如何trigger
- 采集流程
研究背景
主要是网上的资料,关于如何控制采集bootchart时长的方法不一,且尝试过几种方法均无法控制后,遂看了下Android中bootchart源码,分析了生成header、proc_diskstats、proc_ps、proc_stat的过程。
已有的时长控制的方法:
-
echo $TIME_OUT > /data/bootchart/start
其中的$TIME_OUT
为时长,比如采集50s,echo 50 > /data/bootchart/start
-
echo $TIME_OUT> /data/bootchart-start
与方法1一致,$TIME_OUT
为时长。
测试了下,针对我的手机+虚拟机,两种方法均无法控制时长,所以看了下Android源码,分析了下原因。
采集时长控制
通过查看Android源码,发现针对Android-5、6-7、8+版本中bootchart生成变化较多,针对差异较大的时长进行了分析。
Android 5
先上源码链接bootchart.c,bootchart.h,挑出关于时间控制的源码。
- 宏定义文件名
#define LOG_STARTFILE "/data/bootchart-start"
- 如果
/data/bootchart-start
文件中有数值,且不为0,设置timeout
buff[0] = 0;
proc_read( LOG_STARTFILE, buff, sizeof(buff) );
if (buff[0] != 0) {
timeout = atoi(buff);
}
- 如果
/data/bootchart-start
文件中timeout超过10*60,则设置timeout为10*60。再将timeout转化为具体的count,通过采集的index去跳出采集。
# define BOOTCHART_MAX_TIME_SEC (10*60) /* max polling time in seconds */
# define BOOTCHART_POLLING_MS 200 /* polling period in ms */
if (timeout == 0)
return 0;
if (timeout > BOOTCHART_MAX_TIME_SEC)
timeout = BOOTCHART_MAX_TIME_SEC;
count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
- 采集中停止采集
可以通过向/data/bootchart-stop
写入1即可,echo 1 > /data/bootchart-stop
。
#define LOG_STOPFILE "/data/bootchart-stop"
/* called each time you want to perform a bootchart sampling op */
int bootchart_step( void )
{
do_log_file(log_stat, "/proc/stat");
do_log_file(log_disks, "/proc/diskstats");
do_log_procs(log_procs);
/* we stop when /data/bootchart-stop contains 1 */
{
char buff[2];
if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
return -1;
}
}
return 0;
}
Android 6-7
这两个版本中关于bootchart的相对于Android 5有所变化,同样先上源码链接bootchart.cpp,以Android 7.1.0举例。
- 宏定义文件名,控制逻辑与Android 5 类似。
#define LOG_ROOT "/data/bootchart"
#define LOG_STARTFILE LOG_ROOT"/start"
#define LOG_STOPFILE LOG_ROOT"/stop"
Android 8+
Android 8+ 的源代码中去除了手动时长控制,查看源代码bootchart.cpp和readme文档,可以发现主要是通过init.rc去控制运行、停止。
- init.rc相关的配置,具体启动过程可以参见Android7.0 init.rc流程分析。
on post-fs-data
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
bootchart start
on property:sys.boot_completed=1
bootchart stop
- 启动
默认在post-fs-data trigger触发器中去执行bootchart,依然是判断有/data/bootchart/enabled
文件即可。
int do_bootchart(const std::vector<std::string>& args) {
if (args[1] == "start") return do_bootchart_start();
return do_bootchart_stop();
}
static int do_bootchart_start() {
// We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
std::string start;
if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
LOG(VERBOSE) << "Not bootcharting";
return 0;
}
g_bootcharting_thread = new std::thread(bootchart_thread_main);
return 0;
}
- 停止
sys.boot_completed=1
时,即当系统的属性sys.boot_completed为1的时候(系统启动完成),会触发args[1] = stop
,去停止bootchart采集。
Bootchart源代码分析
针对Android 12,对Bootchart源码进行分析,先上源码,bootchart.cpp,ActivityManagerService.java,README.md。
如何trigger
从readme中,关于Bootchart的描述如下:
/system/etc/init/hw/init.rc is the primary .rc file and is loaded by the init executable at the beginning of its execution. It is responsible for the initial set up of the system.
bootchart [start|stop]
Start/stop bootcharting. These are present in the default init.rc files, but bootcharting is only active if the file /data/bootchart/enabled exists; otherwise bootchart start/stop are no-ops.
可知,是通过init.rc中,去调起bootchart start
和 bootchart stop
的。
打开Android12模拟器下的 /system/etc/init/hw/init.rc
文件,关于bootchart的流程如下:
- 服务启动
一旦/data分区挂载完毕后,立即去启动bootchart服务。
on post-fs-data
mark_post_data
# Start checkpoint before we touch data
exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
# Make sure we have the device encryption key.
installkey /data
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell encryption=Require
bootchart start
- 服务结束
系统启动完成后,停止采集bootchart。
on property:sys.boot_completed=1
bootchart stop
所以系统的属性sys.boot_completed为啥时候为1?
参考Android系统启动之BOOT_COMPLETED广播
查看ActivityManagerService.java
,关于trigger sys.boot_completed
的代码如下:
final void finishBooting() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_ACTIVITY_MANAGER);
t.traceBegin("FinishBooting");
synchronized (this) {
if (!mBootAnimationComplete) {
mCallFinishBooting = true;
return;
}
mCallFinishBooting = false;
}
// Let the ART runtime in zygote and system_server know that the boot completed.
// Inform checkpointing systems of success
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
......
mBootAnimationComplete
即开机动画结束.
采集流程