1*00c7fec1SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*00c7fec1SAndroid Build Coastguard Worker# Copyright (C) 2015 The Android Open Source Project 3*00c7fec1SAndroid Build Coastguard Worker# 4*00c7fec1SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*00c7fec1SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*00c7fec1SAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*00c7fec1SAndroid Build Coastguard Worker# 8*00c7fec1SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*00c7fec1SAndroid Build Coastguard Worker# 10*00c7fec1SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*00c7fec1SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*00c7fec1SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*00c7fec1SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*00c7fec1SAndroid Build Coastguard Worker# limitations under the License. 15*00c7fec1SAndroid Build Coastguard Worker"""Record the event logs during boot and output them to a file. 16*00c7fec1SAndroid Build Coastguard Worker 17*00c7fec1SAndroid Build Coastguard WorkerThis script repeats the record of each event log during Android boot specified 18*00c7fec1SAndroid Build Coastguard Workertimes. By default, interval between measurements is adjusted in such a way that 19*00c7fec1SAndroid Build Coastguard WorkerCPUs are cooled down sufficiently to avoid boot time slowdown caused by CPU 20*00c7fec1SAndroid Build Coastguard Workerthermal throttling. The result is output in a tab-separated value format. 21*00c7fec1SAndroid Build Coastguard Worker 22*00c7fec1SAndroid Build Coastguard WorkerExamples: 23*00c7fec1SAndroid Build Coastguard Worker 24*00c7fec1SAndroid Build Coastguard WorkerRepeat measurements 10 times. Interval between iterations is adjusted based on 25*00c7fec1SAndroid Build Coastguard WorkerCPU temperature of the device. 26*00c7fec1SAndroid Build Coastguard Worker 27*00c7fec1SAndroid Build Coastguard Worker$ ./perfboot.py --iterations=10 28*00c7fec1SAndroid Build Coastguard Worker 29*00c7fec1SAndroid Build Coastguard WorkerRepeat measurements 20 times. 60 seconds interval is taken between each 30*00c7fec1SAndroid Build Coastguard Workeriteration. 31*00c7fec1SAndroid Build Coastguard Worker 32*00c7fec1SAndroid Build Coastguard Worker$ ./perfboot.py --iterations=20 --interval=60 33*00c7fec1SAndroid Build Coastguard Worker 34*00c7fec1SAndroid Build Coastguard WorkerRepeat measurements 20 times, show verbose output, output the result to 35*00c7fec1SAndroid Build Coastguard Workerdata.tsv, and read event tags from eventtags.txt. 36*00c7fec1SAndroid Build Coastguard Worker 37*00c7fec1SAndroid Build Coastguard Worker$ ./perfboot.py --iterations=30 -v --output=data.tsv --tags=eventtags.txt 38*00c7fec1SAndroid Build Coastguard Worker""" 39*00c7fec1SAndroid Build Coastguard Worker 40*00c7fec1SAndroid Build Coastguard Workerimport argparse 41*00c7fec1SAndroid Build Coastguard Workerimport atexit 42*00c7fec1SAndroid Build Coastguard Workerimport io 43*00c7fec1SAndroid Build Coastguard Workerimport glob 44*00c7fec1SAndroid Build Coastguard Workerimport inspect 45*00c7fec1SAndroid Build Coastguard Workerimport logging 46*00c7fec1SAndroid Build Coastguard Workerimport math 47*00c7fec1SAndroid Build Coastguard Workerimport os 48*00c7fec1SAndroid Build Coastguard Workerimport re 49*00c7fec1SAndroid Build Coastguard Workerimport subprocess 50*00c7fec1SAndroid Build Coastguard Workerimport sys 51*00c7fec1SAndroid Build Coastguard Workerimport threading 52*00c7fec1SAndroid Build Coastguard Workerimport time 53*00c7fec1SAndroid Build Coastguard Worker 54*00c7fec1SAndroid Build Coastguard Workersys.path.append(os.path.dirname(os.path.dirname(__file__))) 55*00c7fec1SAndroid Build Coastguard Workerimport adb 56*00c7fec1SAndroid Build Coastguard Worker 57*00c7fec1SAndroid Build Coastguard Worker# The default event tags to record. 58*00c7fec1SAndroid Build Coastguard Worker_DEFAULT_EVENT_TAGS = [ 59*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_start', 60*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_preload_start', 61*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_preload_end', 62*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_system_run', 63*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_pms_start', 64*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_pms_system_scan_start', 65*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_pms_data_scan_start', 66*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_pms_scan_end', 67*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_pms_ready', 68*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_ams_ready', 69*00c7fec1SAndroid Build Coastguard Worker 'boot_progress_enable_screen', 70*00c7fec1SAndroid Build Coastguard Worker 'sf_stop_bootanim', 71*00c7fec1SAndroid Build Coastguard Worker 'wm_boot_animation_done', 72*00c7fec1SAndroid Build Coastguard Worker] 73*00c7fec1SAndroid Build Coastguard Worker 74*00c7fec1SAndroid Build Coastguard Worker 75*00c7fec1SAndroid Build Coastguard Workerclass IntervalAdjuster(object): 76*00c7fec1SAndroid Build Coastguard Worker """A helper class to take suffficient interval between iterations.""" 77*00c7fec1SAndroid Build Coastguard Worker 78*00c7fec1SAndroid Build Coastguard Worker # CPU temperature values per product used to decide interval 79*00c7fec1SAndroid Build Coastguard Worker _CPU_COOL_DOWN_THRESHOLDS = { 80*00c7fec1SAndroid Build Coastguard Worker 'flo': 40, 81*00c7fec1SAndroid Build Coastguard Worker 'flounder': 40000, 82*00c7fec1SAndroid Build Coastguard Worker 'razor': 40, 83*00c7fec1SAndroid Build Coastguard Worker 'volantis': 40000, 84*00c7fec1SAndroid Build Coastguard Worker } 85*00c7fec1SAndroid Build Coastguard Worker # The interval between CPU temperature checks 86*00c7fec1SAndroid Build Coastguard Worker _CPU_COOL_DOWN_WAIT_INTERVAL = 10 87*00c7fec1SAndroid Build Coastguard Worker # The wait time used when the value of _CPU_COOL_DOWN_THRESHOLDS for 88*00c7fec1SAndroid Build Coastguard Worker # the product is not defined. 89*00c7fec1SAndroid Build Coastguard Worker _CPU_COOL_DOWN_WAIT_TIME_DEFAULT = 120 90*00c7fec1SAndroid Build Coastguard Worker 91*00c7fec1SAndroid Build Coastguard Worker def __init__(self, interval, device): 92*00c7fec1SAndroid Build Coastguard Worker self._interval = interval 93*00c7fec1SAndroid Build Coastguard Worker self._device = device 94*00c7fec1SAndroid Build Coastguard Worker self._temp_paths = device.shell( 95*00c7fec1SAndroid Build Coastguard Worker ['ls', '/sys/class/thermal/thermal_zone*/temp'])[0].splitlines() 96*00c7fec1SAndroid Build Coastguard Worker self._product = device.get_prop('ro.build.product') 97*00c7fec1SAndroid Build Coastguard Worker self._waited = False 98*00c7fec1SAndroid Build Coastguard Worker 99*00c7fec1SAndroid Build Coastguard Worker def wait(self): 100*00c7fec1SAndroid Build Coastguard Worker """Waits certain amount of time for CPUs cool-down.""" 101*00c7fec1SAndroid Build Coastguard Worker if self._interval is None: 102*00c7fec1SAndroid Build Coastguard Worker self._wait_cpu_cool_down(self._product, self._temp_paths) 103*00c7fec1SAndroid Build Coastguard Worker else: 104*00c7fec1SAndroid Build Coastguard Worker if self._waited: 105*00c7fec1SAndroid Build Coastguard Worker print('Waiting for %d seconds' % self._interval) 106*00c7fec1SAndroid Build Coastguard Worker time.sleep(self._interval) 107*00c7fec1SAndroid Build Coastguard Worker self._waited = True 108*00c7fec1SAndroid Build Coastguard Worker 109*00c7fec1SAndroid Build Coastguard Worker def _get_cpu_temp(self, threshold): 110*00c7fec1SAndroid Build Coastguard Worker max_temp = 0 111*00c7fec1SAndroid Build Coastguard Worker for temp_path in self._temp_paths: 112*00c7fec1SAndroid Build Coastguard Worker temp = int(self._device.shell(['cat', temp_path])[0].rstrip()) 113*00c7fec1SAndroid Build Coastguard Worker max_temp = max(max_temp, temp) 114*00c7fec1SAndroid Build Coastguard Worker if temp >= threshold: 115*00c7fec1SAndroid Build Coastguard Worker return temp 116*00c7fec1SAndroid Build Coastguard Worker return max_temp 117*00c7fec1SAndroid Build Coastguard Worker 118*00c7fec1SAndroid Build Coastguard Worker def _wait_cpu_cool_down(self, product, temp_paths): 119*00c7fec1SAndroid Build Coastguard Worker threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get( 120*00c7fec1SAndroid Build Coastguard Worker self._product) 121*00c7fec1SAndroid Build Coastguard Worker if threshold is None: 122*00c7fec1SAndroid Build Coastguard Worker print('No CPU temperature threshold is set for ' + self._product) 123*00c7fec1SAndroid Build Coastguard Worker print(('Just wait %d seconds' % 124*00c7fec1SAndroid Build Coastguard Worker IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)) 125*00c7fec1SAndroid Build Coastguard Worker time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT) 126*00c7fec1SAndroid Build Coastguard Worker return 127*00c7fec1SAndroid Build Coastguard Worker while True: 128*00c7fec1SAndroid Build Coastguard Worker temp = self._get_cpu_temp(threshold) 129*00c7fec1SAndroid Build Coastguard Worker if temp < threshold: 130*00c7fec1SAndroid Build Coastguard Worker logging.info('Current CPU temperature %s' % temp) 131*00c7fec1SAndroid Build Coastguard Worker return 132*00c7fec1SAndroid Build Coastguard Worker print('Waiting until CPU temperature (%d) falls below %d' % ( 133*00c7fec1SAndroid Build Coastguard Worker temp, threshold)) 134*00c7fec1SAndroid Build Coastguard Worker time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL) 135*00c7fec1SAndroid Build Coastguard Worker 136*00c7fec1SAndroid Build Coastguard Worker 137*00c7fec1SAndroid Build Coastguard Workerclass WatchdogTimer(object): 138*00c7fec1SAndroid Build Coastguard Worker """A timer that makes is_timedout() return true in |timeout| seconds.""" 139*00c7fec1SAndroid Build Coastguard Worker def __init__(self, timeout): 140*00c7fec1SAndroid Build Coastguard Worker self._timedout = False 141*00c7fec1SAndroid Build Coastguard Worker 142*00c7fec1SAndroid Build Coastguard Worker def notify_timeout(): 143*00c7fec1SAndroid Build Coastguard Worker self._timedout = True 144*00c7fec1SAndroid Build Coastguard Worker self._timer = threading.Timer(timeout, notify_timeout) 145*00c7fec1SAndroid Build Coastguard Worker self._timer.daemon = True 146*00c7fec1SAndroid Build Coastguard Worker self._timer.start() 147*00c7fec1SAndroid Build Coastguard Worker 148*00c7fec1SAndroid Build Coastguard Worker def is_timedout(self): 149*00c7fec1SAndroid Build Coastguard Worker return self._timedout 150*00c7fec1SAndroid Build Coastguard Worker 151*00c7fec1SAndroid Build Coastguard Worker def cancel(self): 152*00c7fec1SAndroid Build Coastguard Worker self._timer.cancel() 153*00c7fec1SAndroid Build Coastguard Worker 154*00c7fec1SAndroid Build Coastguard Worker 155*00c7fec1SAndroid Build Coastguard Workerdef readlines_unbuffered(proc): 156*00c7fec1SAndroid Build Coastguard Worker """Read lines from |proc|'s standard out without buffering.""" 157*00c7fec1SAndroid Build Coastguard Worker while True: 158*00c7fec1SAndroid Build Coastguard Worker buf = [] 159*00c7fec1SAndroid Build Coastguard Worker c = proc.stdout.read(1) 160*00c7fec1SAndroid Build Coastguard Worker if c == '' and proc.poll() is not None: 161*00c7fec1SAndroid Build Coastguard Worker break 162*00c7fec1SAndroid Build Coastguard Worker while c != '\n': 163*00c7fec1SAndroid Build Coastguard Worker if c == '' and proc.poll() is not None: 164*00c7fec1SAndroid Build Coastguard Worker break 165*00c7fec1SAndroid Build Coastguard Worker buf.append(c) 166*00c7fec1SAndroid Build Coastguard Worker c = proc.stdout.read(1) 167*00c7fec1SAndroid Build Coastguard Worker yield ''.join(buf) 168*00c7fec1SAndroid Build Coastguard Worker 169*00c7fec1SAndroid Build Coastguard Worker 170*00c7fec1SAndroid Build Coastguard Workerdef disable_dropbox(device): 171*00c7fec1SAndroid Build Coastguard Worker """Removes the files created by Dropbox and avoids creating the files.""" 172*00c7fec1SAndroid Build Coastguard Worker device.root() 173*00c7fec1SAndroid Build Coastguard Worker device.wait() 174*00c7fec1SAndroid Build Coastguard Worker device.shell(['rm', '-rf', '/system/data/dropbox']) 175*00c7fec1SAndroid Build Coastguard Worker original_dropbox_max_files = device.shell( 176*00c7fec1SAndroid Build Coastguard Worker ['settings', 'get', 'global', 'dropbox_max_files'])[0].rstrip() 177*00c7fec1SAndroid Build Coastguard Worker device.shell(['settings', 'put', 'global', 'dropbox_max_files', '0']) 178*00c7fec1SAndroid Build Coastguard Worker return original_dropbox_max_files 179*00c7fec1SAndroid Build Coastguard Worker 180*00c7fec1SAndroid Build Coastguard Worker 181*00c7fec1SAndroid Build Coastguard Workerdef restore_dropbox(device, original_dropbox_max_files): 182*00c7fec1SAndroid Build Coastguard Worker """Restores the dropbox_max_files setting.""" 183*00c7fec1SAndroid Build Coastguard Worker device.root() 184*00c7fec1SAndroid Build Coastguard Worker device.wait() 185*00c7fec1SAndroid Build Coastguard Worker if original_dropbox_max_files == 'null': 186*00c7fec1SAndroid Build Coastguard Worker device.shell(['settings', 'delete', 'global', 'dropbox_max_files']) 187*00c7fec1SAndroid Build Coastguard Worker else: 188*00c7fec1SAndroid Build Coastguard Worker device.shell(['settings', 'put', 'global', 'dropbox_max_files', 189*00c7fec1SAndroid Build Coastguard Worker original_dropbox_max_files]) 190*00c7fec1SAndroid Build Coastguard Worker 191*00c7fec1SAndroid Build Coastguard Worker 192*00c7fec1SAndroid Build Coastguard Workerdef init_perf(device, output, record_list, tags): 193*00c7fec1SAndroid Build Coastguard Worker device.wait() 194*00c7fec1SAndroid Build Coastguard Worker debuggable = device.get_prop('ro.debuggable') 195*00c7fec1SAndroid Build Coastguard Worker original_dropbox_max_files = None 196*00c7fec1SAndroid Build Coastguard Worker if debuggable == '1': 197*00c7fec1SAndroid Build Coastguard Worker # Workaround for Dropbox issue (http://b/20890386). 198*00c7fec1SAndroid Build Coastguard Worker original_dropbox_max_files = disable_dropbox(device) 199*00c7fec1SAndroid Build Coastguard Worker 200*00c7fec1SAndroid Build Coastguard Worker def cleanup(): 201*00c7fec1SAndroid Build Coastguard Worker try: 202*00c7fec1SAndroid Build Coastguard Worker if record_list: 203*00c7fec1SAndroid Build Coastguard Worker print_summary(record_list, tags[-1]) 204*00c7fec1SAndroid Build Coastguard Worker output_results(output, record_list, tags) 205*00c7fec1SAndroid Build Coastguard Worker if original_dropbox_max_files is not None: 206*00c7fec1SAndroid Build Coastguard Worker restore_dropbox(device, original_dropbox_max_files) 207*00c7fec1SAndroid Build Coastguard Worker except (subprocess.CalledProcessError, RuntimeError): 208*00c7fec1SAndroid Build Coastguard Worker pass 209*00c7fec1SAndroid Build Coastguard Worker atexit.register(cleanup) 210*00c7fec1SAndroid Build Coastguard Worker 211*00c7fec1SAndroid Build Coastguard Worker 212*00c7fec1SAndroid Build Coastguard Workerdef check_dm_verity_settings(device): 213*00c7fec1SAndroid Build Coastguard Worker device.wait() 214*00c7fec1SAndroid Build Coastguard Worker for partition in ['system', 'vendor']: 215*00c7fec1SAndroid Build Coastguard Worker verity_mode = device.get_prop('partition.%s.verified' % partition) 216*00c7fec1SAndroid Build Coastguard Worker if verity_mode is None: 217*00c7fec1SAndroid Build Coastguard Worker logging.warning('dm-verity is not enabled for /%s. Did you run ' 218*00c7fec1SAndroid Build Coastguard Worker 'adb disable-verity? That may skew the result.', 219*00c7fec1SAndroid Build Coastguard Worker partition) 220*00c7fec1SAndroid Build Coastguard Worker 221*00c7fec1SAndroid Build Coastguard Worker 222*00c7fec1SAndroid Build Coastguard Workerdef read_event_tags(tags_file): 223*00c7fec1SAndroid Build Coastguard Worker """Reads event tags from |tags_file|.""" 224*00c7fec1SAndroid Build Coastguard Worker if not tags_file: 225*00c7fec1SAndroid Build Coastguard Worker return _DEFAULT_EVENT_TAGS 226*00c7fec1SAndroid Build Coastguard Worker tags = [] 227*00c7fec1SAndroid Build Coastguard Worker with open(tags_file) as f: 228*00c7fec1SAndroid Build Coastguard Worker for line in f: 229*00c7fec1SAndroid Build Coastguard Worker if '#' in line: 230*00c7fec1SAndroid Build Coastguard Worker line = line[:line.find('#')] 231*00c7fec1SAndroid Build Coastguard Worker line = line.strip() 232*00c7fec1SAndroid Build Coastguard Worker if line: 233*00c7fec1SAndroid Build Coastguard Worker tags.append(line) 234*00c7fec1SAndroid Build Coastguard Worker return tags 235*00c7fec1SAndroid Build Coastguard Worker 236*00c7fec1SAndroid Build Coastguard Worker 237*00c7fec1SAndroid Build Coastguard Workerdef make_event_tags_re(tags): 238*00c7fec1SAndroid Build Coastguard Worker """Makes a regular expression object that matches event logs of |tags|.""" 239*00c7fec1SAndroid Build Coastguard Worker return re.compile(r'(?P<pid>[0-9]+) +[0-9]+ I (?P<tag>%s): (?P<time>\d+)' % 240*00c7fec1SAndroid Build Coastguard Worker '|'.join(tags)) 241*00c7fec1SAndroid Build Coastguard Worker 242*00c7fec1SAndroid Build Coastguard Worker 243*00c7fec1SAndroid Build Coastguard Workerdef filter_event_tags(tags, device): 244*00c7fec1SAndroid Build Coastguard Worker """Drop unknown tags not listed in device's event-log-tags file.""" 245*00c7fec1SAndroid Build Coastguard Worker device.wait() 246*00c7fec1SAndroid Build Coastguard Worker supported_tags = set() 247*00c7fec1SAndroid Build Coastguard Worker for l in device.shell( 248*00c7fec1SAndroid Build Coastguard Worker ['cat', '/system/etc/event-log-tags'])[0].splitlines(): 249*00c7fec1SAndroid Build Coastguard Worker tokens = l.split(' ') 250*00c7fec1SAndroid Build Coastguard Worker if len(tokens) >= 2: 251*00c7fec1SAndroid Build Coastguard Worker supported_tags.add(tokens[1]) 252*00c7fec1SAndroid Build Coastguard Worker filtered = [] 253*00c7fec1SAndroid Build Coastguard Worker for tag in tags: 254*00c7fec1SAndroid Build Coastguard Worker if tag in supported_tags: 255*00c7fec1SAndroid Build Coastguard Worker filtered.append(tag) 256*00c7fec1SAndroid Build Coastguard Worker else: 257*00c7fec1SAndroid Build Coastguard Worker logging.warning('Unknown tag \'%s\'. Ignoring...', tag) 258*00c7fec1SAndroid Build Coastguard Worker return filtered 259*00c7fec1SAndroid Build Coastguard Worker 260*00c7fec1SAndroid Build Coastguard Worker 261*00c7fec1SAndroid Build Coastguard Workerdef get_values(record, tag): 262*00c7fec1SAndroid Build Coastguard Worker """Gets values that matches |tag| from |record|.""" 263*00c7fec1SAndroid Build Coastguard Worker keys = [key for key in list(record.keys()) if key[0] == tag] 264*00c7fec1SAndroid Build Coastguard Worker return [record[k] for k in sorted(keys)] 265*00c7fec1SAndroid Build Coastguard Worker 266*00c7fec1SAndroid Build Coastguard Worker 267*00c7fec1SAndroid Build Coastguard Workerdef get_last_value(record, tag): 268*00c7fec1SAndroid Build Coastguard Worker """Gets the last value that matches |tag| from |record|.""" 269*00c7fec1SAndroid Build Coastguard Worker values = get_values(record, tag) 270*00c7fec1SAndroid Build Coastguard Worker if not values: 271*00c7fec1SAndroid Build Coastguard Worker return 0 272*00c7fec1SAndroid Build Coastguard Worker return values[-1] 273*00c7fec1SAndroid Build Coastguard Worker 274*00c7fec1SAndroid Build Coastguard Worker 275*00c7fec1SAndroid Build Coastguard Workerdef output_results(filename, record_list, tags): 276*00c7fec1SAndroid Build Coastguard Worker """Outputs |record_list| into |filename| in a TSV format.""" 277*00c7fec1SAndroid Build Coastguard Worker # First, count the number of the values of each tag. 278*00c7fec1SAndroid Build Coastguard Worker # This is for dealing with events that occur multiple times. 279*00c7fec1SAndroid Build Coastguard Worker # For instance, boot_progress_preload_start and boot_progress_preload_end 280*00c7fec1SAndroid Build Coastguard Worker # are recorded twice on 64-bit system. One is for 64-bit zygote process 281*00c7fec1SAndroid Build Coastguard Worker # and the other is for 32-bit zygote process. 282*00c7fec1SAndroid Build Coastguard Worker values_counter = {} 283*00c7fec1SAndroid Build Coastguard Worker for record in record_list: 284*00c7fec1SAndroid Build Coastguard Worker for tag in tags: 285*00c7fec1SAndroid Build Coastguard Worker # Some record might lack values for some tags due to unanticipated 286*00c7fec1SAndroid Build Coastguard Worker # problems (e.g. timeout), so take the maximum count among all the 287*00c7fec1SAndroid Build Coastguard Worker # record. 288*00c7fec1SAndroid Build Coastguard Worker values_counter[tag] = max(values_counter.get(tag, 1), 289*00c7fec1SAndroid Build Coastguard Worker len(get_values(record, tag))) 290*00c7fec1SAndroid Build Coastguard Worker 291*00c7fec1SAndroid Build Coastguard Worker # Then creates labels for the data. If there are multiple values for one 292*00c7fec1SAndroid Build Coastguard Worker # tag, labels for these values are numbered except the first one as 293*00c7fec1SAndroid Build Coastguard Worker # follows: 294*00c7fec1SAndroid Build Coastguard Worker # 295*00c7fec1SAndroid Build Coastguard Worker # event_tag event_tag2 event_tag3 296*00c7fec1SAndroid Build Coastguard Worker # 297*00c7fec1SAndroid Build Coastguard Worker # The corresponding values are sorted in an ascending order of PID. 298*00c7fec1SAndroid Build Coastguard Worker labels = [] 299*00c7fec1SAndroid Build Coastguard Worker for tag in tags: 300*00c7fec1SAndroid Build Coastguard Worker for i in range(1, values_counter[tag] + 1): 301*00c7fec1SAndroid Build Coastguard Worker labels.append('%s%s' % (tag, '' if i == 1 else str(i))) 302*00c7fec1SAndroid Build Coastguard Worker 303*00c7fec1SAndroid Build Coastguard Worker # Finally write the data into the file. 304*00c7fec1SAndroid Build Coastguard Worker with open(filename, 'w') as f: 305*00c7fec1SAndroid Build Coastguard Worker f.write('\t'.join(labels) + '\n') 306*00c7fec1SAndroid Build Coastguard Worker for record in record_list: 307*00c7fec1SAndroid Build Coastguard Worker line = io.StringIO() 308*00c7fec1SAndroid Build Coastguard Worker invalid_line = False 309*00c7fec1SAndroid Build Coastguard Worker for i, tag in enumerate(tags): 310*00c7fec1SAndroid Build Coastguard Worker if i != 0: 311*00c7fec1SAndroid Build Coastguard Worker line.write('\t') 312*00c7fec1SAndroid Build Coastguard Worker values = get_values(record, tag) 313*00c7fec1SAndroid Build Coastguard Worker if len(values) < values_counter[tag]: 314*00c7fec1SAndroid Build Coastguard Worker invalid_line = True 315*00c7fec1SAndroid Build Coastguard Worker # Fill invalid record with 0 316*00c7fec1SAndroid Build Coastguard Worker values += [0] * (values_counter[tag] - len(values)) 317*00c7fec1SAndroid Build Coastguard Worker line.write('\t'.join(str(t) for t in values)) 318*00c7fec1SAndroid Build Coastguard Worker if invalid_line: 319*00c7fec1SAndroid Build Coastguard Worker logging.error('Invalid record found: ' + line.getvalue()) 320*00c7fec1SAndroid Build Coastguard Worker line.write('\n') 321*00c7fec1SAndroid Build Coastguard Worker f.write(line.getvalue()) 322*00c7fec1SAndroid Build Coastguard Worker print(('Wrote: ' + filename)) 323*00c7fec1SAndroid Build Coastguard Worker 324*00c7fec1SAndroid Build Coastguard Worker 325*00c7fec1SAndroid Build Coastguard Workerdef median(data): 326*00c7fec1SAndroid Build Coastguard Worker """Calculates the median value from |data|.""" 327*00c7fec1SAndroid Build Coastguard Worker data = sorted(data) 328*00c7fec1SAndroid Build Coastguard Worker n = len(data) 329*00c7fec1SAndroid Build Coastguard Worker if n % 2 == 1: 330*00c7fec1SAndroid Build Coastguard Worker return data[n / 2] 331*00c7fec1SAndroid Build Coastguard Worker else: 332*00c7fec1SAndroid Build Coastguard Worker n2 = n / 2 333*00c7fec1SAndroid Build Coastguard Worker return (data[n2 - 1] + data[n2]) / 2.0 334*00c7fec1SAndroid Build Coastguard Worker 335*00c7fec1SAndroid Build Coastguard Worker 336*00c7fec1SAndroid Build Coastguard Workerdef mean(data): 337*00c7fec1SAndroid Build Coastguard Worker """Calculates the mean value from |data|.""" 338*00c7fec1SAndroid Build Coastguard Worker return float(sum(data)) / len(data) 339*00c7fec1SAndroid Build Coastguard Worker 340*00c7fec1SAndroid Build Coastguard Worker 341*00c7fec1SAndroid Build Coastguard Workerdef stddev(data): 342*00c7fec1SAndroid Build Coastguard Worker """Calculates the standard deviation value from |value|.""" 343*00c7fec1SAndroid Build Coastguard Worker m = mean(data) 344*00c7fec1SAndroid Build Coastguard Worker return math.sqrt(sum((x - m) ** 2 for x in data) / len(data)) 345*00c7fec1SAndroid Build Coastguard Worker 346*00c7fec1SAndroid Build Coastguard Worker 347*00c7fec1SAndroid Build Coastguard Workerdef print_summary(record_list, end_tag): 348*00c7fec1SAndroid Build Coastguard Worker """Prints the summary of |record_list|.""" 349*00c7fec1SAndroid Build Coastguard Worker # Filter out invalid data. 350*00c7fec1SAndroid Build Coastguard Worker end_times = [get_last_value(record, end_tag) for record in record_list 351*00c7fec1SAndroid Build Coastguard Worker if get_last_value(record, end_tag) != 0] 352*00c7fec1SAndroid Build Coastguard Worker print(('mean:', int(round(mean(end_times))), 'ms')) 353*00c7fec1SAndroid Build Coastguard Worker print(('median:', int(round(median(end_times))), 'ms')) 354*00c7fec1SAndroid Build Coastguard Worker print(('standard deviation:', int(round(stddev(end_times))), 'ms')) 355*00c7fec1SAndroid Build Coastguard Worker 356*00c7fec1SAndroid Build Coastguard Worker 357*00c7fec1SAndroid Build Coastguard Workerdef do_iteration(device, interval_adjuster, event_tags_re, end_tag): 358*00c7fec1SAndroid Build Coastguard Worker """Measures the boot time once.""" 359*00c7fec1SAndroid Build Coastguard Worker device.wait() 360*00c7fec1SAndroid Build Coastguard Worker interval_adjuster.wait() 361*00c7fec1SAndroid Build Coastguard Worker device.reboot() 362*00c7fec1SAndroid Build Coastguard Worker print('Rebooted the device, waiting for tag', end_tag) 363*00c7fec1SAndroid Build Coastguard Worker record = {} 364*00c7fec1SAndroid Build Coastguard Worker booted = False 365*00c7fec1SAndroid Build Coastguard Worker while not booted: 366*00c7fec1SAndroid Build Coastguard Worker device.wait() 367*00c7fec1SAndroid Build Coastguard Worker # Stop the iteration if it does not finish within 120 seconds. 368*00c7fec1SAndroid Build Coastguard Worker timeout = 120 369*00c7fec1SAndroid Build Coastguard Worker t = WatchdogTimer(timeout) 370*00c7fec1SAndroid Build Coastguard Worker p = subprocess.Popen( 371*00c7fec1SAndroid Build Coastguard Worker ['adb', 'logcat', '-b', 'events', '-v', 'threadtime'], 372*00c7fec1SAndroid Build Coastguard Worker stdout=subprocess.PIPE) 373*00c7fec1SAndroid Build Coastguard Worker for line in readlines_unbuffered(p): 374*00c7fec1SAndroid Build Coastguard Worker if t.is_timedout(): 375*00c7fec1SAndroid Build Coastguard Worker print('*** Timed out ***') 376*00c7fec1SAndroid Build Coastguard Worker return record 377*00c7fec1SAndroid Build Coastguard Worker m = event_tags_re.search(line) 378*00c7fec1SAndroid Build Coastguard Worker if not m: 379*00c7fec1SAndroid Build Coastguard Worker continue 380*00c7fec1SAndroid Build Coastguard Worker tag = m.group('tag') 381*00c7fec1SAndroid Build Coastguard Worker event_time = int(m.group('time')) 382*00c7fec1SAndroid Build Coastguard Worker pid = m.group('pid') 383*00c7fec1SAndroid Build Coastguard Worker record[(tag, pid)] = event_time 384*00c7fec1SAndroid Build Coastguard Worker print(('Event log recorded: %s (%s) - %d ms' % ( 385*00c7fec1SAndroid Build Coastguard Worker tag, pid, event_time))) 386*00c7fec1SAndroid Build Coastguard Worker if tag == end_tag: 387*00c7fec1SAndroid Build Coastguard Worker booted = True 388*00c7fec1SAndroid Build Coastguard Worker t.cancel() 389*00c7fec1SAndroid Build Coastguard Worker break 390*00c7fec1SAndroid Build Coastguard Worker return record 391*00c7fec1SAndroid Build Coastguard Worker 392*00c7fec1SAndroid Build Coastguard Worker 393*00c7fec1SAndroid Build Coastguard Workerdef parse_args(): 394*00c7fec1SAndroid Build Coastguard Worker """Parses the command line arguments.""" 395*00c7fec1SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 396*00c7fec1SAndroid Build Coastguard Worker description=inspect.getdoc(sys.modules[__name__]), 397*00c7fec1SAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter) 398*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('--iterations', type=int, default=5, 399*00c7fec1SAndroid Build Coastguard Worker help='Number of times to repeat boot measurements.') 400*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('--interval', type=int, 401*00c7fec1SAndroid Build Coastguard Worker help=('Duration between iterations. If this is not ' 402*00c7fec1SAndroid Build Coastguard Worker 'set explicitly, durations are determined ' 403*00c7fec1SAndroid Build Coastguard Worker 'adaptively based on CPUs temperature.')) 404*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('-o', '--output', help='File name of output data.') 405*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('-v', '--verbose', action='store_true', 406*00c7fec1SAndroid Build Coastguard Worker help='Show verbose output.') 407*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('-s', '--serial', default=os.getenv('ANDROID_SERIAL'), 408*00c7fec1SAndroid Build Coastguard Worker help='Adb device serial number.') 409*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('-t', '--tags', help='Specify the filename from which ' 410*00c7fec1SAndroid Build Coastguard Worker 'event tags are read. Every line contains one event ' 411*00c7fec1SAndroid Build Coastguard Worker 'tag and the last event tag is used to detect that ' 412*00c7fec1SAndroid Build Coastguard Worker 'the device has finished booting unless --end-tag is ' 413*00c7fec1SAndroid Build Coastguard Worker 'specified.') 414*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('--end-tag', help='An event tag on which the script ' 415*00c7fec1SAndroid Build Coastguard Worker 'stops measuring the boot time.') 416*00c7fec1SAndroid Build Coastguard Worker parser.add_argument('--apk-dir', help='Specify the directory which contains ' 417*00c7fec1SAndroid Build Coastguard Worker 'APK files to be installed before measuring boot time.') 418*00c7fec1SAndroid Build Coastguard Worker return parser.parse_args() 419*00c7fec1SAndroid Build Coastguard Worker 420*00c7fec1SAndroid Build Coastguard Worker 421*00c7fec1SAndroid Build Coastguard Workerdef install_apks(device, apk_dir): 422*00c7fec1SAndroid Build Coastguard Worker for apk in glob.glob(os.path.join(apk_dir, '*.apk')): 423*00c7fec1SAndroid Build Coastguard Worker print('Installing: ' + apk) 424*00c7fec1SAndroid Build Coastguard Worker device.install(apk, replace=True) 425*00c7fec1SAndroid Build Coastguard Worker 426*00c7fec1SAndroid Build Coastguard Worker 427*00c7fec1SAndroid Build Coastguard Workerdef main(): 428*00c7fec1SAndroid Build Coastguard Worker args = parse_args() 429*00c7fec1SAndroid Build Coastguard Worker if args.verbose: 430*00c7fec1SAndroid Build Coastguard Worker logging.getLogger().setLevel(logging.INFO) 431*00c7fec1SAndroid Build Coastguard Worker 432*00c7fec1SAndroid Build Coastguard Worker device = adb.get_device(args.serial) 433*00c7fec1SAndroid Build Coastguard Worker 434*00c7fec1SAndroid Build Coastguard Worker if not args.output: 435*00c7fec1SAndroid Build Coastguard Worker device.wait() 436*00c7fec1SAndroid Build Coastguard Worker args.output = 'perf-%s-%s.tsv' % ( 437*00c7fec1SAndroid Build Coastguard Worker device.get_prop('ro.build.flavor'), 438*00c7fec1SAndroid Build Coastguard Worker device.get_prop('ro.build.version.incremental')) 439*00c7fec1SAndroid Build Coastguard Worker check_dm_verity_settings(device) 440*00c7fec1SAndroid Build Coastguard Worker 441*00c7fec1SAndroid Build Coastguard Worker if args.apk_dir: 442*00c7fec1SAndroid Build Coastguard Worker install_apks(device, args.apk_dir) 443*00c7fec1SAndroid Build Coastguard Worker 444*00c7fec1SAndroid Build Coastguard Worker record_list = [] 445*00c7fec1SAndroid Build Coastguard Worker event_tags = filter_event_tags(read_event_tags(args.tags), device) 446*00c7fec1SAndroid Build Coastguard Worker end_tag = args.end_tag or event_tags[-1] 447*00c7fec1SAndroid Build Coastguard Worker if end_tag not in event_tags: 448*00c7fec1SAndroid Build Coastguard Worker sys.exit('%s is not a valid tag.' % end_tag) 449*00c7fec1SAndroid Build Coastguard Worker event_tags = event_tags[0 : event_tags.index(end_tag) + 1] 450*00c7fec1SAndroid Build Coastguard Worker init_perf(device, args.output, record_list, event_tags) 451*00c7fec1SAndroid Build Coastguard Worker interval_adjuster = IntervalAdjuster(args.interval, device) 452*00c7fec1SAndroid Build Coastguard Worker event_tags_re = make_event_tags_re(event_tags) 453*00c7fec1SAndroid Build Coastguard Worker 454*00c7fec1SAndroid Build Coastguard Worker for i in range(args.iterations): 455*00c7fec1SAndroid Build Coastguard Worker print('Run #%d ' % i) 456*00c7fec1SAndroid Build Coastguard Worker record = do_iteration( 457*00c7fec1SAndroid Build Coastguard Worker device, interval_adjuster, event_tags_re, end_tag) 458*00c7fec1SAndroid Build Coastguard Worker record_list.append(record) 459*00c7fec1SAndroid Build Coastguard Worker 460*00c7fec1SAndroid Build Coastguard Worker 461*00c7fec1SAndroid Build Coastguard Workerif __name__ == '__main__': 462*00c7fec1SAndroid Build Coastguard Worker main() 463