• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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()
147