blob: 99f988df29fe3cc5bc97ebc4aabef52c2b35ce02 [file] [log] [blame]
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +01001#!/usr/bin/env python3
2# Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
6import argparse
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +02007import datetime
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +01008import os
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +02009import re
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020010import statistics
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010011import sys
12import time
13
14try:
15 from perfetto.trace_processor import TraceProcessor
16except ImportError:
17 sys.exit(
18 'Unable to analyze perfetto trace without the perfetto library. '
19 'Install instructions:\n'
20 ' sudo apt install python3-pip\n'
21 ' pip3 install perfetto')
22
23sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
24
25import adb_utils
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +020026import apk_utils
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010027import perfetto_utils
28import utils
29
30def setup(options):
31 # Increase screen off timeout to avoid device screen turns off.
32 twenty_four_hours_in_millis = 24 * 60 * 60 * 1000
33 previous_screen_off_timeout = adb_utils.get_screen_off_timeout(
34 options.device_id)
35 adb_utils.set_screen_off_timeout(
36 twenty_four_hours_in_millis, options.device_id)
37
38 # Unlock device.
39 adb_utils.unlock(options.device_id, options.device_pin)
40
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020041 teardown_options = {
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010042 'previous_screen_off_timeout': previous_screen_off_timeout
43 }
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020044 return teardown_options
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010045
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020046def teardown(options, teardown_options):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010047 # Reset screen off timeout.
48 adb_utils.set_screen_off_timeout(
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020049 teardown_options['previous_screen_off_timeout'],
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010050 options.device_id)
51
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020052def run_all(apk_or_apks, options, tmp_dir):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010053 # Launch app while collecting information.
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020054 data_total = {}
55 for iteration in range(1, options.iterations + 1):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010056 print('Starting iteration %i' % iteration)
57 out_dir = os.path.join(options.out_dir, str(iteration))
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020058 teardown_options = setup_for_run(apk_or_apks, out_dir, options)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010059 data = run(out_dir, options, tmp_dir)
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +020060 teardown_for_run(out_dir, options, teardown_options)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020061 add_data(data_total, data)
62 print('Result:')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010063 print(data)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020064 print(compute_data_summary(data_total))
65 print('Done')
66 print('Average result:')
67 data_summary = compute_data_summary(data_total)
68 print(data_summary)
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +010069 write_data_to_dir(options.out_dir, data_summary)
70 if options.out:
71 write_data_to_file(options.out, data_summary)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010072
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020073def compute_data_summary(data_total):
74 data_summary = {}
75 for key, value in data_total.items():
76 if not isinstance(value, list):
77 data_summary[key] = value
78 continue
79 data_summary['%s_avg' % key] = round(statistics.mean(value), 1)
80 data_summary['%s_med' % key] = statistics.median(value)
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +010081 data_summary['%s_min' % key] = min(value)
82 data_summary['%s_max' % key] = max(value)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020083 return data_summary
84
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020085def setup_for_run(apk_or_apks, out_dir, options):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010086 adb_utils.root(options.device_id)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020087
88 print('Installing')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010089 adb_utils.uninstall(options.app_id, options.device_id)
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020090 if apk_or_apks['apk']:
91 adb_utils.install(apk_or_apks['apk'], options.device_id)
92 else:
93 assert apk_or_apks['apks']
94 adb_utils.install_apks(apk_or_apks['apks'], options.device_id)
95
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020096 os.makedirs(out_dir, exist_ok=True)
97
98 # AOT compile.
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010099 if options.aot:
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200100 print('AOT compiling')
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200101 if options.baseline_profile:
102 adb_utils.clear_profile_data(options.app_id, options.device_id)
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100103 if options.baseline_profile_install == 'adb':
104 adb_utils.install_profile_using_adb(
105 options.app_id, options.baseline_profile, options.device_id)
106 else:
107 assert options.baseline_profile_install == 'profileinstaller'
108 adb_utils.install_profile_using_profileinstaller(
109 options.app_id, options.device_id)
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200110 else:
111 adb_utils.force_compilation(options.app_id, options.device_id)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200112
113 # Cooldown and then unlock device.
114 if options.cooldown > 0:
115 print('Cooling down for %i seconds' % options.cooldown)
116 assert adb_utils.get_screen_state(options.device_id).is_off()
117 time.sleep(options.cooldown)
118 teardown_options = adb_utils.prepare_for_interaction_with_device(
119 options.device_id, options.device_pin)
Christoffer Quist Adamsen7a0014f2022-05-19 08:43:00 +0200120 else:
121 teardown_options = None
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200122
123 # Prelaunch for hot startup.
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200124 if options.hot_startup:
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200125 print('Prelaunching')
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200126 adb_utils.launch_activity(
127 options.app_id,
128 options.main_activity,
129 options.device_id,
130 wait_for_activity_to_launch=False)
131 time.sleep(options.startup_duration)
132 adb_utils.navigate_to_home_screen(options.device_id)
133 time.sleep(1)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200134
135 # Drop caches before run.
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100136 adb_utils.drop_caches(options.device_id)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200137 return teardown_options
138
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200139def teardown_for_run(out_dir, options, teardown_options):
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200140 assert adb_utils.get_screen_state(options.device_id).is_on_and_unlocked()
141
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200142 if options.capture_screen:
143 target = os.path.join(out_dir, 'screen.png')
144 adb_utils.capture_screen(target, options.device_id)
145
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200146 if options.cooldown > 0:
147 adb_utils.teardown_after_interaction_with_device(
148 teardown_options, options.device_id)
149 adb_utils.ensure_screen_off(options.device_id)
150 else:
151 assert teardown_options is None
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100152
153def run(out_dir, options, tmp_dir):
Christoffer Quist Adamsen2d5d9632022-04-06 14:48:30 +0200154 assert adb_utils.get_screen_state(options.device_id).is_on_and_unlocked()
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100155
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200156 # Start logcat for time to fully drawn.
157 logcat_process = None
158 if options.fully_drawn_logcat_message:
159 adb_utils.clear_logcat(options.device_id)
160 logcat_process = adb_utils.start_logcat(
161 options.device_id,
162 format='time',
163 filter='%s ActivityTaskManager:I' % options.fully_drawn_logcat_filter,
164 silent=True)
165
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100166 # Start perfetto trace collector.
Christoffer Quist Adamsen2d5d9632022-04-06 14:48:30 +0200167 perfetto_process = None
168 perfetto_trace_path = None
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200169 if options.perfetto:
Christoffer Quist Adamsen2d5d9632022-04-06 14:48:30 +0200170 perfetto_process, perfetto_trace_path = perfetto_utils.record_android_trace(
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200171 out_dir, tmp_dir, options.device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100172
173 # Launch main activity.
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100174 launch_activity_result = adb_utils.launch_activity(
175 options.app_id,
176 options.main_activity,
177 options.device_id,
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200178 intent_data_uri=options.intent_data_uri,
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100179 wait_for_activity_to_launch=True)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100180
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200181 # Wait for app to be fully drawn.
182 logcat = None
183 if logcat_process is not None:
184 wait_until_fully_drawn(logcat_process, options)
185 logcat = adb_utils.stop_logcat(logcat_process)
186
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100187 # Wait for perfetto trace collector to stop.
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200188 if options.perfetto:
Christoffer Quist Adamsen2d5d9632022-04-06 14:48:30 +0200189 perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100190
191 # Get minor and major page faults from app process.
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200192 data = compute_data(
193 launch_activity_result, logcat, perfetto_trace_path, options)
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100194 write_data_to_dir(out_dir, data)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100195 return data
196
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200197def wait_until_fully_drawn(logcat_process, options):
198 print('Waiting until app is fully drawn')
199 while True:
200 is_fully_drawn = any(
201 is_app_fully_drawn_logcat_message(line, options) \
202 for line in logcat_process.lines)
203 if is_fully_drawn:
204 break
205 time.sleep(1)
206 print('Done')
207
208def compute_time_to_fully_drawn_from_time_to_first_frame(logcat, options):
209 displayed_time = None
210 fully_drawn_time = None
211 for line in logcat:
212 if is_app_displayed_logcat_message(line, options):
213 displayed_time = get_timestamp_from_logcat_message(line)
214 elif is_app_fully_drawn_logcat_message(line, options):
215 fully_drawn_time = get_timestamp_from_logcat_message(line)
216 assert displayed_time is not None
217 assert fully_drawn_time is not None
218 assert fully_drawn_time > displayed_time
219 return fully_drawn_time - displayed_time
220
221def get_timestamp_from_logcat_message(line):
222 time_end_index = len('00-00 00:00:00.000')
223 time_format = '%m-%d %H:%M:%S.%f'
224 time_str = line[0:time_end_index] + '000'
225 time_seconds = datetime.datetime.strptime(time_str, time_format).timestamp()
226 return int(time_seconds * 1000)
227
228def is_app_displayed_logcat_message(line, options):
229 substring = 'Displayed %s' % adb_utils.get_component_name(
230 options.app_id, options.main_activity)
231 return substring in line
232
233def is_app_fully_drawn_logcat_message(line, options):
234 return re.search(options.fully_drawn_logcat_message, line)
235
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200236def add_data(data_total, data):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100237 for key, value in data.items():
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200238 if key == 'app_id':
239 assert data_total.get(key, value) == value
240 data_total[key] = value
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100241 if key == 'time':
242 continue
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200243 if key in data_total:
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100244 if key == 'app_id':
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200245 assert data_total[key] == value
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100246 else:
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200247 existing_value = data_total[key]
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100248 assert isinstance(value, int)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200249 assert isinstance(existing_value, list)
250 existing_value.append(value)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100251 else:
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200252 assert isinstance(value, int), key
253 data_total[key] = [value]
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100254
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200255def compute_data(launch_activity_result, logcat, perfetto_trace_path, options):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100256 minfl, majfl = adb_utils.get_minor_major_page_faults(
257 options.app_id, options.device_id)
Christoffer Quist Adamsen558db202022-08-18 14:32:06 +0200258 meminfo = adb_utils.get_meminfo(options.app_id, options.device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100259 data = {
260 'app_id': options.app_id,
261 'time': time.ctime(time.time()),
262 'minfl': minfl,
263 'majfl': majfl
264 }
Christoffer Quist Adamsen558db202022-08-18 14:32:06 +0200265 data.update(meminfo)
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100266 startup_data = compute_startup_data(
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200267 launch_activity_result, logcat, perfetto_trace_path, options)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100268 return data | startup_data
269
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200270def compute_startup_data(
271 launch_activity_result, logcat, perfetto_trace_path, options):
272 time_to_first_frame = launch_activity_result.get('total_time')
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100273 startup_data = {
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200274 'adb_startup': time_to_first_frame
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100275 }
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200276
277 # Time to fully drawn.
278 if options.fully_drawn_logcat_message:
279 startup_data['time_to_fully_drawn'] = \
280 compute_time_to_fully_drawn_from_time_to_first_frame(logcat, options) \
281 + time_to_first_frame
282
283 # Perfetto stats.
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100284 perfetto_startup_data = {}
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200285 if options.perfetto:
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100286 trace_processor = TraceProcessor(file_path=perfetto_trace_path)
287
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200288 # Compute time to first frame according to the builtin android_startup
289 # metric.
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100290 startup_metric = trace_processor.metric(['android_startup'])
291 time_to_first_frame_ms = \
292 startup_metric.android_startup.startup[0].to_first_frame.dur_ms
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200293 perfetto_startup_data['perfetto_startup'] = round(time_to_first_frame_ms)
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100294
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200295 if not options.hot_startup:
296 # Compute time to first and last doFrame event.
297 bind_application_slice = perfetto_utils.find_unique_slice_by_name(
298 'bindApplication', options, trace_processor)
299 activity_start_slice = perfetto_utils.find_unique_slice_by_name(
300 'activityStart', options, trace_processor)
301 do_frame_slices = perfetto_utils.find_slices_by_name(
302 'Choreographer#doFrame', options, trace_processor)
303 first_do_frame_slice = next(do_frame_slices)
304 *_, last_do_frame_slice = do_frame_slices
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100305
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200306 perfetto_startup_data.update({
307 'time_to_first_choreographer_do_frame_ms':
308 round(perfetto_utils.get_slice_end_since_start(
309 first_do_frame_slice, bind_application_slice)),
310 'time_to_last_choreographer_do_frame_ms':
311 round(perfetto_utils.get_slice_end_since_start(
312 last_do_frame_slice, bind_application_slice))
313 })
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100314
315 # Return combined startup data.
316 return startup_data | perfetto_startup_data
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100317
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100318def write_data_to_dir(out_dir, data):
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100319 data_path = os.path.join(out_dir, 'data.txt')
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100320 write_data_to_file(data_path, data)
321
322def write_data_to_file(out_file, data):
323 with open(out_file, 'w') as f:
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100324 for key, value in data.items():
325 f.write('%s=%s\n' % (key, str(value)))
326
327def parse_options(argv):
328 result = argparse.ArgumentParser(
329 description='Generate a perfetto trace file.')
330 result.add_argument('--app-id',
331 help='The application ID of interest',
332 required=True)
333 result.add_argument('--aot',
334 help='Enable force compilation',
335 default=False,
336 action='store_true')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100337 result.add_argument('--apk',
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200338 help='Path to the .apk')
339 result.add_argument('--apks',
340 help='Path to the .apks')
341 result.add_argument('--bundle',
342 help='Path to the .aab')
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200343 result.add_argument('--capture-screen',
344 help='Take a screenshot after each test',
345 default=False,
346 action='store_true')
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200347 result.add_argument('--cooldown',
348 help='Seconds to wait before running each iteration',
349 default=0,
350 type=int)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100351 result.add_argument('--device-id',
352 help='Device id (e.g., emulator-5554).')
353 result.add_argument('--device-pin',
354 help='Device pin code (e.g., 1234)')
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200355 result.add_argument('--fully-drawn-logcat-filter',
356 help='Logcat filter for the fully drawn message '
357 '(e.g., "tag:I")')
358 result.add_argument('--fully-drawn-logcat-message',
359 help='Logcat message that indicates that the app is '
360 'fully drawn (regexp)')
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200361 result.add_argument('--hot-startup',
362 help='Measure hot startup instead of cold startup',
363 default=False,
364 action='store_true')
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200365 result.add_argument('--intent-data-uri',
366 help='Value to use for the -d argument to the intent '
367 'that is used to launch the app')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100368 result.add_argument('--iterations',
369 help='Number of traces to generate',
370 default=1,
371 type=int)
372 result.add_argument('--main-activity',
373 help='Main activity class name',
374 required=True)
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100375 result.add_argument('--no-perfetto',
376 help='Disables perfetto trace generation',
377 action='store_true',
378 default=False)
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100379 result.add_argument('--out',
380 help='File to store result in')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100381 result.add_argument('--out-dir',
382 help='Directory to store trace files in',
383 required=True)
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200384 result.add_argument('--baseline-profile',
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100385 help='Baseline profile (.prof) in binary format')
386 result.add_argument('--baseline-profile-metadata',
387 help='Baseline profile metadata (.profm) in binary '
388 'format')
389 result.add_argument('--baseline-profile-install',
390 help='Whether to install profile using adb or '
391 'profileinstaller',
392 choices=['adb', 'profileinstaller'],
393 default='profileinstaller')
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200394 result.add_argument('--startup-duration',
395 help='Duration in seconds before shutting down app',
396 default=15,
397 type=int)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100398 options, args = result.parse_known_args(argv)
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200399 setattr(options, 'perfetto', not options.no_perfetto)
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200400
401 paths = [
402 path for path in [options.apk, options.apks, options.bundle]
403 if path is not None]
404 assert len(paths) == 1, 'Expected exactly one .apk, .apks, or .aab file.'
405
406 # Build .apks file up front to avoid building the bundle upon each install.
407 if options.bundle:
408 os.makedirs(options.out_dir, exist_ok=True)
409 options.apks = os.path.join(options.out_dir, 'Bundle.apks')
410 adb_utils.build_apks_from_bundle(
411 options.bundle, options.apks, overwrite=True)
412 del options.bundle
413
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200414 # Profile is only used with --aot.
415 assert options.aot or not options.baseline_profile
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200416
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200417 # Fully drawn logcat filter and message is absent or both present.
418 assert (options.fully_drawn_logcat_filter is None) == \
419 (options.fully_drawn_logcat_message is None)
420
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100421 return options, args
422
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200423def global_setup(options):
424 # If there is no cooldown then unlock the screen once. Otherwise we turn off
425 # the screen during the cooldown and unlock the screen before each iteration.
426 teardown_options = None
427 if options.cooldown == 0:
428 teardown_options = adb_utils.prepare_for_interaction_with_device(
429 options.device_id, options.device_pin)
430 assert adb_utils.get_screen_state(options.device_id).is_on()
431 else:
432 adb_utils.ensure_screen_off(options.device_id)
433 return teardown_options
434
435def global_teardown(options, teardown_options):
436 if options.cooldown == 0:
437 adb_utils.teardown_after_interaction_with_device(
438 teardown_options, options.device_id)
439 else:
440 assert teardown_options is None
441
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100442def main(argv):
443 (options, args) = parse_options(argv)
444 with utils.TempDir() as tmp_dir:
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200445 apk_or_apks = { 'apk': options.apk, 'apks': options.apks }
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100446 if options.baseline_profile \
447 and options.baseline_profile_install == 'profileinstaller':
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200448 assert not options.apks, 'Unimplemented'
449 apk_or_apks['apk'] = apk_utils.add_baseline_profile_to_apk(
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100450 options.apk,
451 options.baseline_profile,
452 options.baseline_profile_metadata,
453 tmp_dir)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200454 teardown_options = global_setup(options)
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200455 run_all(apk_or_apks, options, tmp_dir)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200456 global_teardown(options, teardown_options)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100457
458if __name__ == '__main__':
459 sys.exit(main(sys.argv[1:]))