blob: 2057b5518107c834907fb54a267eac51141ae675 [file] [log] [blame]
Ben Cheng50bbde02015-06-09 17:04:36 +08001#!/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
19This script extracts two bootchart.tgz files and compares the timestamps
20in proc_ps.log for selected processes. The proc_ps.log file consists of
21repetitive blocks of the following format:
22
23timestamp1 (jiffies)
24dumps of /proc/<pid>/stat
25
26timestamp2
27dumps of /proc/<pid>/stat
28
29The timestamps are 200ms apart, and the creation time of selected processes
30are listed. The termination time of the boot animation process is also listed
31as a coarse indication about when the boot process is complete as perceived by
32the user.
33"""
34
35import sys
36import 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.
41jiffy_to_wallclock = {
42 '1st_timestamp': -1,
43 '2nd_timestamp': -1,
44 'jiffy_to_wallclock': -1
45}
46
47def 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
78def 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
134def 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
145if __name__ == "__main__":
146 main()