Skip to content

Commit 50bbde0

Browse files
author
Ben Cheng
committed
Compare two bootcharts and list timestamps for selected processes.
Usage: system/core/init/compare-bootcharts.py base_bootchart_dir exp_bootchart_dir For example, here is the output where the bootanimation is changed from "d 0 0 part2" to "c 0 0 part2": -- process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system) ------------------------------------ /init: 50 40 (-10) /system/bin/surfaceflinger: 4320 4470 (+150) /system/bin/bootanimation: 6980 6990 (+10) zygote64: 10410 10640 (+230) zygote: 10410 10640 (+230) system_server: 15350 15150 (-200) bootanimation ends at: 33790 31230 (-2560) -- In this example bootanimation is finished (estimated) 2.56 seconds sooner. Change-Id: I39d59897c8c53d7d662676813e884b9d58feec3c
1 parent e106191 commit 50bbde0

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

init/compare-bootcharts.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright (C) 2015 The Android Open Source Project
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""Compare two bootcharts and list start/end timestamps on key processes.
18+
19+
This script extracts two bootchart.tgz files and compares the timestamps
20+
in proc_ps.log for selected processes. The proc_ps.log file consists of
21+
repetitive blocks of the following format:
22+
23+
timestamp1 (jiffies)
24+
dumps of /proc/<pid>/stat
25+
26+
timestamp2
27+
dumps of /proc/<pid>/stat
28+
29+
The timestamps are 200ms apart, and the creation time of selected processes
30+
are listed. The termination time of the boot animation process is also listed
31+
as a coarse indication about when the boot process is complete as perceived by
32+
the user.
33+
"""
34+
35+
import sys
36+
import tarfile
37+
38+
# The bootchart timestamps are 200ms apart, but the USER_HZ value is not
39+
# reported in the bootchart, so we use the first two timestamps to calculate
40+
# the wall clock time of a jiffy.
41+
jiffy_to_wallclock = {
42+
'1st_timestamp': -1,
43+
'2nd_timestamp': -1,
44+
'jiffy_to_wallclock': -1
45+
}
46+
47+
def analyze_process_maps(process_map1, process_map2, jiffy_record):
48+
# List interesting processes here
49+
processes_of_interest = [
50+
'/init',
51+
'/system/bin/surfaceflinger',
52+
'/system/bin/bootanimation',
53+
'zygote64',
54+
'zygote',
55+
'system_server'
56+
]
57+
58+
jw = jiffy_record['jiffy_to_wallclock']
59+
print "process: baseline experiment (delta)"
60+
print " - Unit is ms (a jiffy is %d ms on the system)" % jw
61+
print "------------------------------------"
62+
for p in processes_of_interest:
63+
# e.g., 32-bit system doesn't have zygote64
64+
if p in process_map1 and p in process_map2:
65+
print "%s: %d %d (%+d)" % (
66+
p, process_map1[p]['start_time'] * jw,
67+
process_map2[p]['start_time'] * jw,
68+
(process_map2[p]['start_time'] -
69+
process_map1[p]['start_time']) * jw)
70+
71+
# Print the last tick for the bootanimation process
72+
print "bootanimation ends at: %d %d (%+d)" % (
73+
process_map1['/system/bin/bootanimation']['last_tick'] * jw,
74+
process_map2['/system/bin/bootanimation']['last_tick'] * jw,
75+
(process_map2['/system/bin/bootanimation']['last_tick'] -
76+
process_map1['/system/bin/bootanimation']['last_tick']) * jw)
77+
78+
def parse_proc_file(pathname, process_map, jiffy_record=None):
79+
# Uncompress bootchart.tgz
80+
with tarfile.open(pathname + '/bootchart.tgz', 'r:*') as tf:
81+
try:
82+
# Read proc_ps.log
83+
f = tf.extractfile('proc_ps.log')
84+
85+
# Break proc_ps into chunks based on timestamps
86+
blocks = f.read().split('\n\n')
87+
for b in blocks:
88+
lines = b.split('\n')
89+
if not lines[0]:
90+
break
91+
92+
# 200ms apart in jiffies
93+
timestamp = int(lines[0]);
94+
95+
# Figure out the wall clock time of a jiffy
96+
if jiffy_record is not None:
97+
if jiffy_record['1st_timestamp'] == -1:
98+
jiffy_record['1st_timestamp'] = timestamp
99+
elif jiffy_record['jiffy_to_wallclock'] == -1:
100+
# Not really needed but for debugging purposes
101+
jiffy_record['2nd_timestamp'] = timestamp
102+
value = 200 / (timestamp -
103+
jiffy_record['1st_timestamp'])
104+
# Fix the rounding error
105+
# e.g., 201 jiffies in 200ms when USER_HZ is 1000
106+
if value == 0:
107+
value = 1
108+
# e.g., 21 jiffies in 200ms when USER_HZ is 100
109+
elif value == 9:
110+
value = 10
111+
jiffy_record['jiffy_to_wallclock'] = value
112+
113+
# Populate the process_map table
114+
for line in lines[1:]:
115+
segs = line.split(' ')
116+
117+
# 0: pid
118+
# 1: process name
119+
# 17: priority
120+
# 18: nice
121+
# 21: creation time
122+
123+
proc_name = segs[1].strip('()')
124+
if proc_name in process_map:
125+
process = process_map[proc_name]
126+
else:
127+
process = {'start_time': int(segs[21])}
128+
process_map[proc_name] = process
129+
130+
process['last_tick'] = timestamp
131+
finally:
132+
f.close()
133+
134+
def main():
135+
if len(sys.argv) != 3:
136+
print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
137+
sys.exit(1)
138+
139+
process_map1 = {}
140+
process_map2 = {}
141+
parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)
142+
parse_proc_file(sys.argv[2], process_map2)
143+
analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)
144+
145+
if __name__ == "__main__":
146+
main()

init/readme.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,29 @@ running at 0s. You'll have to look at dmesg to work out when the kernel
347347
actually started init.
348348

349349

350+
Comparing two bootcharts
351+
------------------------
352+
A handy script named compare-bootcharts.py can be used to compare the
353+
start/end time of selected processes. The aforementioned grab-bootchart.sh
354+
will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
355+
If two such barballs are preserved on the host machine under different
356+
directories, the script can list the timestamps differences. For example:
357+
358+
Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
359+
exp_bootchart_dir
360+
361+
process: baseline experiment (delta)
362+
- Unit is ms (a jiffy is 10 ms on the system)
363+
------------------------------------
364+
/init: 50 40 (-10)
365+
/system/bin/surfaceflinger: 4320 4470 (+150)
366+
/system/bin/bootanimation: 6980 6990 (+10)
367+
zygote64: 10410 10640 (+230)
368+
zygote: 10410 10640 (+230)
369+
system_server: 15350 15150 (-200)
370+
bootanimation ends at: 33790 31230 (-2560)
371+
372+
350373
Debugging init
351374
--------------
352375
By default, programs executed by init will drop stdout and stderr into

0 commit comments

Comments
 (0)