Format python files using yapf
Change-Id: I8b7b97efb6bfdcceef9efc533cdaa0675ab7db40
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index adae239..211be89 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -11,445 +11,474 @@
import sys
import time
+
class Device:
- def __init__(self, device_id, device_pin):
- self.device_id = device_id
- self.device_pin = device_pin
+ def __init__(self, device_id, device_pin):
+ self.device_id = device_id
+ self.device_pin = device_pin
+
def extend_startup_descriptors(startup_descriptors, iteration, device, options):
- (logcat, profile, profile_classes_and_methods) = \
- generate_startup_profile(device, options)
- if options.logcat:
- write_tmp_logcat(logcat, iteration, options)
- current_startup_descriptors = get_r8_startup_descriptors_from_logcat(
- logcat, options)
- else:
- write_tmp_profile(profile, iteration, options)
- write_tmp_profile_classes_and_methods(
- profile_classes_and_methods, iteration, options)
- current_startup_descriptors = \
- profile_utils.transform_art_profile_to_r8_startup_list(
- profile_classes_and_methods, options.generalize_synthetics)
- write_tmp_startup_descriptors(current_startup_descriptors, iteration, options)
- new_startup_descriptors = add_r8_startup_descriptors(
- startup_descriptors, current_startup_descriptors)
- number_of_new_startup_descriptors = \
- len(new_startup_descriptors) - len(startup_descriptors)
- if options.out is not None:
- print(
- 'Found %i new startup descriptors in iteration %i'
- % (number_of_new_startup_descriptors, iteration + 1))
- return new_startup_descriptors
+ (logcat, profile, profile_classes_and_methods) = \
+ generate_startup_profile(device, options)
+ if options.logcat:
+ write_tmp_logcat(logcat, iteration, options)
+ current_startup_descriptors = get_r8_startup_descriptors_from_logcat(
+ logcat, options)
+ else:
+ write_tmp_profile(profile, iteration, options)
+ write_tmp_profile_classes_and_methods(profile_classes_and_methods,
+ iteration, options)
+ current_startup_descriptors = \
+ profile_utils.transform_art_profile_to_r8_startup_list(
+ profile_classes_and_methods, options.generalize_synthetics)
+ write_tmp_startup_descriptors(current_startup_descriptors, iteration,
+ options)
+ new_startup_descriptors = add_r8_startup_descriptors(
+ startup_descriptors, current_startup_descriptors)
+ number_of_new_startup_descriptors = \
+ len(new_startup_descriptors) - len(startup_descriptors)
+ if options.out is not None:
+ print('Found %i new startup descriptors in iteration %i' %
+ (number_of_new_startup_descriptors, iteration + 1))
+ return new_startup_descriptors
+
def generate_startup_profile(device, options):
- logcat = None
- profile = None
- profile_classes_and_methods = None
- if options.use_existing_profile:
- # Verify presence of profile.
- adb_utils.check_app_has_profile_data(options.app_id, device.device_id)
- profile = adb_utils.get_profile_data(options.app_id, device.device_id)
- profile_classes_and_methods = \
- adb_utils.get_classes_and_methods_from_app_profile(
- options.app_id, device.device_id)
- else:
- # Unlock device.
- tear_down_options = adb_utils.prepare_for_interaction_with_device(
- device.device_id, device.device_pin)
-
- logcat_process = None
- if options.logcat:
- # Clear logcat and start capturing logcat.
- adb_utils.clear_logcat(device.device_id)
- logcat_process = adb_utils.start_logcat(
- device.device_id, format='tag', filter='R8:I ActivityTaskManager:I *:S')
+ logcat = None
+ profile = None
+ profile_classes_and_methods = None
+ if options.use_existing_profile:
+ # Verify presence of profile.
+ adb_utils.check_app_has_profile_data(options.app_id, device.device_id)
+ profile = adb_utils.get_profile_data(options.app_id, device.device_id)
+ profile_classes_and_methods = \
+ adb_utils.get_classes_and_methods_from_app_profile(
+ options.app_id, device.device_id)
else:
- # Clear existing profile data.
- adb_utils.clear_profile_data(options.app_id, device.device_id)
+ # Unlock device.
+ tear_down_options = adb_utils.prepare_for_interaction_with_device(
+ device.device_id, device.device_pin)
- # Launch activity to generate startup profile on device.
- adb_utils.launch_activity(
- options.app_id, options.main_activity, device.device_id)
+ logcat_process = None
+ if options.logcat:
+ # Clear logcat and start capturing logcat.
+ adb_utils.clear_logcat(device.device_id)
+ logcat_process = adb_utils.start_logcat(
+ device.device_id,
+ format='tag',
+ filter='R8:I ActivityTaskManager:I *:S')
+ else:
+ # Clear existing profile data.
+ adb_utils.clear_profile_data(options.app_id, device.device_id)
- # Wait for activity startup.
- time.sleep(options.startup_duration)
+ # Launch activity to generate startup profile on device.
+ adb_utils.launch_activity(options.app_id, options.main_activity,
+ device.device_id)
- if options.logcat:
- # Get startup descriptors from logcat.
- logcat = adb_utils.stop_logcat(logcat_process)
- else:
- # Capture startup profile.
- adb_utils.capture_app_profile_data(options.app_id, device.device_id)
- profile = adb_utils.get_profile_data(options.app_id, device.device_id)
- profile_classes_and_methods = \
- adb_utils.get_classes_and_methods_from_app_profile(
- options.app_id, device.device_id)
+ # Wait for activity startup.
+ time.sleep(options.startup_duration)
- # Shutdown app.
- adb_utils.stop_app(options.app_id, device.device_id)
- adb_utils.teardown_after_interaction_with_device(
- tear_down_options, device.device_id)
+ if options.logcat:
+ # Get startup descriptors from logcat.
+ logcat = adb_utils.stop_logcat(logcat_process)
+ else:
+ # Capture startup profile.
+ adb_utils.capture_app_profile_data(options.app_id, device.device_id)
+ profile = adb_utils.get_profile_data(options.app_id,
+ device.device_id)
+ profile_classes_and_methods = \
+ adb_utils.get_classes_and_methods_from_app_profile(
+ options.app_id, device.device_id)
- return (logcat, profile, profile_classes_and_methods)
+ # Shutdown app.
+ adb_utils.stop_app(options.app_id, device.device_id)
+ adb_utils.teardown_after_interaction_with_device(
+ tear_down_options, device.device_id)
+
+ return (logcat, profile, profile_classes_and_methods)
+
def get_r8_startup_descriptors_from_logcat(logcat, options):
- post_startup = False
- startup_descriptors = {}
- for line in logcat:
- line_elements = parse_logcat_line(line)
- if line_elements is None:
- continue
- (priority, tag, message) = line_elements
- if tag == 'ActivityTaskManager':
- if message.startswith('START') \
- or message.startswith('Activity pause timeout for') \
- or message.startswith('Activity top resumed state loss timeout for') \
- or message.startswith('Force removing') \
- or message.startswith(
- 'Launch timeout has expired, giving up wake lock!'):
- continue
- elif message.startswith('Displayed %s/' % options.app_id):
- print('Entering post startup: %s' % message)
- post_startup = True
- continue
- elif tag == 'R8':
- if is_startup_descriptor(message):
- startup_descriptors[message] = {
- 'conditional_startup': False,
- 'hot': False,
- 'post_startup': post_startup,
- 'startup': True
- }
- continue
- # Reaching here means we didn't expect this line.
- report_unrecognized_logcat_line(line)
- return startup_descriptors
+ post_startup = False
+ startup_descriptors = {}
+ for line in logcat:
+ line_elements = parse_logcat_line(line)
+ if line_elements is None:
+ continue
+ (priority, tag, message) = line_elements
+ if tag == 'ActivityTaskManager':
+ if message.startswith('START') \
+ or message.startswith('Activity pause timeout for') \
+ or message.startswith('Activity top resumed state loss timeout for') \
+ or message.startswith('Force removing') \
+ or message.startswith(
+ 'Launch timeout has expired, giving up wake lock!'):
+ continue
+ elif message.startswith('Displayed %s/' % options.app_id):
+ print('Entering post startup: %s' % message)
+ post_startup = True
+ continue
+ elif tag == 'R8':
+ if is_startup_descriptor(message):
+ startup_descriptors[message] = {
+ 'conditional_startup': False,
+ 'hot': False,
+ 'post_startup': post_startup,
+ 'startup': True
+ }
+ continue
+ # Reaching here means we didn't expect this line.
+ report_unrecognized_logcat_line(line)
+ return startup_descriptors
+
def is_startup_descriptor(string):
- # The descriptor should start with the holder (possibly prefixed with 'S').
- if not any(string.startswith('%sL' % flags) for flags in ['', 'S']):
- return False
- # The descriptor should end with ';', a primitive type, or void.
- if not string.endswith(';') \
- and not any(string.endswith(c) for c in get_primitive_descriptors()) \
- and not string.endswith('V'):
- return False
- return True
+ # The descriptor should start with the holder (possibly prefixed with 'S').
+ if not any(string.startswith('%sL' % flags) for flags in ['', 'S']):
+ return False
+ # The descriptor should end with ';', a primitive type, or void.
+ if not string.endswith(';') \
+ and not any(string.endswith(c) for c in get_primitive_descriptors()) \
+ and not string.endswith('V'):
+ return False
+ return True
+
def get_primitive_descriptors():
- return ['Z', 'B', 'S', 'C', 'I', 'F', 'J', 'D']
+ return ['Z', 'B', 'S', 'C', 'I', 'F', 'J', 'D']
+
def parse_logcat_line(line):
- if line == '--------- beginning of kernel':
- return None
- if line == '--------- beginning of main':
- return None
- if line == '--------- beginning of system':
- return None
+ if line == '--------- beginning of kernel':
+ return None
+ if line == '--------- beginning of main':
+ return None
+ if line == '--------- beginning of system':
+ return None
- priority = None
- tag = None
+ priority = None
+ tag = None
- try:
- priority_end = line.index('/')
- priority = line[0:priority_end]
- line = line[priority_end + 1:]
- except ValueError:
- return report_unrecognized_logcat_line(line)
+ try:
+ priority_end = line.index('/')
+ priority = line[0:priority_end]
+ line = line[priority_end + 1:]
+ except ValueError:
+ return report_unrecognized_logcat_line(line)
- try:
- tag_end = line.index(':')
- tag = line[0:tag_end].strip()
- line = line[tag_end + 1 :]
- except ValueError:
- return report_unrecognized_logcat_line(line)
+ try:
+ tag_end = line.index(':')
+ tag = line[0:tag_end].strip()
+ line = line[tag_end + 1:]
+ except ValueError:
+ return report_unrecognized_logcat_line(line)
- message = line.strip()
- return (priority, tag, message)
+ message = line.strip()
+ return (priority, tag, message)
+
def report_unrecognized_logcat_line(line):
- print('Unrecognized line in logcat: %s' % line)
+ print('Unrecognized line in logcat: %s' % line)
-def add_r8_startup_descriptors(old_startup_descriptors, startup_descriptors_to_add):
- new_startup_descriptors = {}
- if len(old_startup_descriptors) == 0:
- for startup_descriptor, flags in startup_descriptors_to_add.items():
- new_startup_descriptors[startup_descriptor] = flags.copy()
- else:
- # Merge the new startup descriptors with the old descriptors in a way so
- # that new startup descriptors are added next to the startup descriptors
- # they are close to in the newly generated list of startup descriptors.
- startup_descriptors_to_add_after_key = {}
- startup_descriptors_to_add_in_the_end = {}
- closest_seen_startup_descriptor = None
- for startup_descriptor, flags in startup_descriptors_to_add.items():
- if startup_descriptor in old_startup_descriptors:
- closest_seen_startup_descriptor = startup_descriptor
- else:
- if closest_seen_startup_descriptor is None:
- # Insert this new startup descriptor in the end of the result.
- startup_descriptors_to_add_in_the_end[startup_descriptor] = flags
- else:
- # Record that this should be inserted after
- # closest_seen_startup_descriptor.
- pending_startup_descriptors = \
- startup_descriptors_to_add_after_key.setdefault(
- closest_seen_startup_descriptor, {})
- pending_startup_descriptors[startup_descriptor] = flags
- for startup_descriptor, flags in old_startup_descriptors.items():
- # Merge flags if this also exists in startup_descriptors_to_add.
- if startup_descriptor in startup_descriptors_to_add:
- merged_flags = flags.copy()
- other_flags = startup_descriptors_to_add[startup_descriptor]
- assert not other_flags['conditional_startup']
- merged_flags['hot'] = \
- merged_flags['hot'] or other_flags['hot']
- merged_flags['startup'] = \
- merged_flags['startup'] or other_flags['startup']
- merged_flags['post_startup'] = \
- merged_flags['post_startup'] or other_flags['post_startup']
- new_startup_descriptors[startup_descriptor] = merged_flags
- else:
- new_startup_descriptors[startup_descriptor] = flags.copy()
- # Flush startup descriptors that followed this item in the new trace.
- if startup_descriptor in startup_descriptors_to_add_after_key:
- pending_startup_descriptors = \
- startup_descriptors_to_add_after_key[startup_descriptor]
- for pending_startup_descriptor, pending_flags \
- in pending_startup_descriptors.items():
- new_startup_descriptors[pending_startup_descriptor] = \
- pending_flags.copy()
- # Insert remaining new startup descriptors in the end.
- for startup_descriptor, flags \
- in startup_descriptors_to_add_in_the_end.items():
- assert startup_descriptor not in new_startup_descriptors
- new_startup_descriptors[startup_descriptor] = flags.copy()
- return new_startup_descriptors
+
+def add_r8_startup_descriptors(old_startup_descriptors,
+ startup_descriptors_to_add):
+ new_startup_descriptors = {}
+ if len(old_startup_descriptors) == 0:
+ for startup_descriptor, flags in startup_descriptors_to_add.items():
+ new_startup_descriptors[startup_descriptor] = flags.copy()
+ else:
+ # Merge the new startup descriptors with the old descriptors in a way so
+ # that new startup descriptors are added next to the startup descriptors
+ # they are close to in the newly generated list of startup descriptors.
+ startup_descriptors_to_add_after_key = {}
+ startup_descriptors_to_add_in_the_end = {}
+ closest_seen_startup_descriptor = None
+ for startup_descriptor, flags in startup_descriptors_to_add.items():
+ if startup_descriptor in old_startup_descriptors:
+ closest_seen_startup_descriptor = startup_descriptor
+ else:
+ if closest_seen_startup_descriptor is None:
+ # Insert this new startup descriptor in the end of the result.
+ startup_descriptors_to_add_in_the_end[
+ startup_descriptor] = flags
+ else:
+ # Record that this should be inserted after
+ # closest_seen_startup_descriptor.
+ pending_startup_descriptors = \
+ startup_descriptors_to_add_after_key.setdefault(
+ closest_seen_startup_descriptor, {})
+ pending_startup_descriptors[startup_descriptor] = flags
+ for startup_descriptor, flags in old_startup_descriptors.items():
+ # Merge flags if this also exists in startup_descriptors_to_add.
+ if startup_descriptor in startup_descriptors_to_add:
+ merged_flags = flags.copy()
+ other_flags = startup_descriptors_to_add[startup_descriptor]
+ assert not other_flags['conditional_startup']
+ merged_flags['hot'] = \
+ merged_flags['hot'] or other_flags['hot']
+ merged_flags['startup'] = \
+ merged_flags['startup'] or other_flags['startup']
+ merged_flags['post_startup'] = \
+ merged_flags['post_startup'] or other_flags['post_startup']
+ new_startup_descriptors[startup_descriptor] = merged_flags
+ else:
+ new_startup_descriptors[startup_descriptor] = flags.copy()
+ # Flush startup descriptors that followed this item in the new trace.
+ if startup_descriptor in startup_descriptors_to_add_after_key:
+ pending_startup_descriptors = \
+ startup_descriptors_to_add_after_key[startup_descriptor]
+ for pending_startup_descriptor, pending_flags \
+ in pending_startup_descriptors.items():
+ new_startup_descriptors[pending_startup_descriptor] = \
+ pending_flags.copy()
+ # Insert remaining new startup descriptors in the end.
+ for startup_descriptor, flags \
+ in startup_descriptors_to_add_in_the_end.items():
+ assert startup_descriptor not in new_startup_descriptors
+ new_startup_descriptors[startup_descriptor] = flags.copy()
+ return new_startup_descriptors
+
def write_tmp_binary_artifact(artifact, iteration, options, name):
- if not options.tmp_dir:
- return
- out_dir = os.path.join(options.tmp_dir, str(iteration))
- os.makedirs(out_dir, exist_ok=True)
- path = os.path.join(out_dir, name)
- with open(path, 'wb') as f:
- f.write(artifact)
+ if not options.tmp_dir:
+ return
+ out_dir = os.path.join(options.tmp_dir, str(iteration))
+ os.makedirs(out_dir, exist_ok=True)
+ path = os.path.join(out_dir, name)
+ with open(path, 'wb') as f:
+ f.write(artifact)
-def write_tmp_textual_artifact(artifact, iteration, options, name, item_to_string=None):
- if not options.tmp_dir:
- return
- out_dir = os.path.join(options.tmp_dir, str(iteration))
- os.makedirs(out_dir, exist_ok=True)
- path = os.path.join(out_dir, name)
- with open(path, 'w') as f:
- for item in artifact:
- f.write(item if item_to_string is None else item_to_string(item))
- f.write('\n')
+
+def write_tmp_textual_artifact(artifact,
+ iteration,
+ options,
+ name,
+ item_to_string=None):
+ if not options.tmp_dir:
+ return
+ out_dir = os.path.join(options.tmp_dir, str(iteration))
+ os.makedirs(out_dir, exist_ok=True)
+ path = os.path.join(out_dir, name)
+ with open(path, 'w') as f:
+ for item in artifact:
+ f.write(item if item_to_string is None else item_to_string(item))
+ f.write('\n')
+
def write_tmp_logcat(logcat, iteration, options):
- write_tmp_textual_artifact(logcat, iteration, options, 'logcat.txt')
+ write_tmp_textual_artifact(logcat, iteration, options, 'logcat.txt')
+
def write_tmp_profile(profile, iteration, options):
- write_tmp_binary_artifact(profile, iteration, options, 'primary.prof')
+ write_tmp_binary_artifact(profile, iteration, options, 'primary.prof')
-def write_tmp_profile_classes_and_methods(
- profile_classes_and_methods, iteration, options):
- def item_to_string(item):
- (descriptor, flags) = item
- return '%s%s%s%s' % (
- 'H' if flags.get('hot') else '',
- 'S' if flags.get('startup') else '',
- 'P' if flags.get('post_startup') else '',
- descriptor)
- write_tmp_textual_artifact(
- profile_classes_and_methods.items(),
- iteration,
- options,
- 'profile.txt',
- item_to_string)
+
+def write_tmp_profile_classes_and_methods(profile_classes_and_methods,
+ iteration, options):
+
+ def item_to_string(item):
+ (descriptor, flags) = item
+ return '%s%s%s%s' % ('H' if flags.get('hot') else '',
+ 'S' if flags.get('startup') else '', 'P'
+ if flags.get('post_startup') else '', descriptor)
+
+ write_tmp_textual_artifact(profile_classes_and_methods.items(), iteration,
+ options, 'profile.txt', item_to_string)
+
def write_tmp_startup_descriptors(startup_descriptors, iteration, options):
- lines = [
- startup_descriptor_to_string(startup_descriptor, flags)
- for startup_descriptor, flags in startup_descriptors.items()]
- write_tmp_textual_artifact(
- lines, iteration, options, 'startup-descriptors.txt')
+ lines = [
+ startup_descriptor_to_string(startup_descriptor, flags)
+ for startup_descriptor, flags in startup_descriptors.items()
+ ]
+ write_tmp_textual_artifact(lines, iteration, options,
+ 'startup-descriptors.txt')
+
def startup_descriptor_to_string(startup_descriptor, flags):
- result = ''
- if flags['hot']:
- result += 'H'
- if flags['startup']:
- result += 'S'
- if flags['post_startup']:
- result += 'P'
- result += startup_descriptor
- return result
+ result = ''
+ if flags['hot']:
+ result += 'H'
+ if flags['startup']:
+ result += 'S'
+ if flags['post_startup']:
+ result += 'P'
+ result += startup_descriptor
+ return result
+
def should_include_startup_descriptor(descriptor, flags, options):
- if flags.get('conditional_startup') \
- and not options.include_conditional_startup:
- return False
- if flags.get('post_startup') \
- and not flags.get('startup') \
- and not options.include_post_startup:
- return False
- return True
+ if flags.get('conditional_startup') \
+ and not options.include_conditional_startup:
+ return False
+ if flags.get('post_startup') \
+ and not flags.get('startup') \
+ and not options.include_post_startup:
+ return False
+ return True
+
def parse_options(argv):
- result = argparse.ArgumentParser(
- description='Generate a perfetto trace file.')
- result.add_argument('--apk',
- help='Path to the .apk')
- result.add_argument('--apks',
- help='Path to the .apks')
- result.add_argument('--app-id',
- help='The application ID of interest',
- required=True)
- result.add_argument('--bundle',
- help='Path to the .aab')
- result.add_argument('--device-id',
- help='Device id (e.g., emulator-5554).',
- action='append')
- result.add_argument('--device-pin',
- help='Device pin code (e.g., 1234)',
- action='append')
- result.add_argument('--generalize-synthetics',
- help='Whether synthetics should be abstracted into their '
- 'synthetic contexts',
- action='store_true',
- default=False)
- result.add_argument('--grant-post-notification-permission',
- help='Grants the android.permission.POST_NOTIFICATIONS '
- 'permission before launching the app',
- default=False,
- action='store_true')
- result.add_argument('--logcat',
- action='store_true',
- default=False)
- result.add_argument('--include-conditional-startup',
- help='Include conditional startup classes and methods in '
- 'the R8 startup descriptors',
- action='store_true',
- default=False)
- result.add_argument('--include-post-startup',
- help='Include post startup classes and methods in the R8 '
- 'startup descriptors',
- action='store_true',
- default=False)
- result.add_argument('--iterations',
- help='Number of profiles to generate',
- default=1,
- type=int)
- result.add_argument('--main-activity',
- help='Main activity class name')
- result.add_argument('--out',
- help='File where to store startup descriptors (defaults '
- 'to stdout)')
- result.add_argument('--startup-duration',
- help='Duration in seconds before shutting down app',
- default=15,
- type=int)
- result.add_argument('--tmp-dir',
- help='Directory where to store intermediate artifacts'
- ' (by default these are not emitted)')
- result.add_argument('--until-stable',
- help='Repeat profile generation until no new startup '
- 'descriptors are found',
- action='store_true',
- default=False)
- result.add_argument('--until-stable-iterations',
- help='Number of times that profile generation must must '
- 'not find new startup descriptors before exiting',
- default=1,
- type=int)
- result.add_argument('--use-existing-profile',
- help='Do not launch app to generate startup profile',
- action='store_true',
- default=False)
- options, args = result.parse_known_args(argv)
+ result = argparse.ArgumentParser(
+ description='Generate a perfetto trace file.')
+ result.add_argument('--apk', help='Path to the .apk')
+ result.add_argument('--apks', help='Path to the .apks')
+ result.add_argument('--app-id',
+ help='The application ID of interest',
+ required=True)
+ result.add_argument('--bundle', help='Path to the .aab')
+ result.add_argument('--device-id',
+ help='Device id (e.g., emulator-5554).',
+ action='append')
+ result.add_argument('--device-pin',
+ help='Device pin code (e.g., 1234)',
+ action='append')
+ result.add_argument(
+ '--generalize-synthetics',
+ help='Whether synthetics should be abstracted into their '
+ 'synthetic contexts',
+ action='store_true',
+ default=False)
+ result.add_argument('--grant-post-notification-permission',
+ help='Grants the android.permission.POST_NOTIFICATIONS '
+ 'permission before launching the app',
+ default=False,
+ action='store_true')
+ result.add_argument('--logcat', action='store_true', default=False)
+ result.add_argument(
+ '--include-conditional-startup',
+ help='Include conditional startup classes and methods in '
+ 'the R8 startup descriptors',
+ action='store_true',
+ default=False)
+ result.add_argument(
+ '--include-post-startup',
+ help='Include post startup classes and methods in the R8 '
+ 'startup descriptors',
+ action='store_true',
+ default=False)
+ result.add_argument('--iterations',
+ help='Number of profiles to generate',
+ default=1,
+ type=int)
+ result.add_argument('--main-activity', help='Main activity class name')
+ result.add_argument(
+ '--out',
+ help='File where to store startup descriptors (defaults '
+ 'to stdout)')
+ result.add_argument('--startup-duration',
+ help='Duration in seconds before shutting down app',
+ default=15,
+ type=int)
+ result.add_argument('--tmp-dir',
+ help='Directory where to store intermediate artifacts'
+ ' (by default these are not emitted)')
+ result.add_argument('--until-stable',
+ help='Repeat profile generation until no new startup '
+ 'descriptors are found',
+ action='store_true',
+ default=False)
+ result.add_argument(
+ '--until-stable-iterations',
+ help='Number of times that profile generation must must '
+ 'not find new startup descriptors before exiting',
+ default=1,
+ type=int)
+ result.add_argument('--use-existing-profile',
+ help='Do not launch app to generate startup profile',
+ action='store_true',
+ default=False)
+ options, args = result.parse_known_args(argv)
- # Read the device pins.
- device_pins = options.device_pin or []
- del options.device_pin
+ # Read the device pins.
+ device_pins = options.device_pin or []
+ del options.device_pin
- # Convert the device ids and pins into a list of devices.
- options.devices = []
- if options.device_id is None:
- # Assume a single device is attached.
- options.devices.append(
- Device(None, device_pins[0] if len(device_pins) > 0 else None))
- else:
- for i in range(len(options.device_id)):
- device_id = options.device_id[i]
- device_pin = device_pins[i] if i < len(device_pins) else None
- options.devices.append(Device(device_id, device_pin))
- del options.device_id
+ # Convert the device ids and pins into a list of devices.
+ options.devices = []
+ if options.device_id is None:
+ # Assume a single device is attached.
+ options.devices.append(
+ Device(None, device_pins[0] if len(device_pins) > 0 else None))
+ else:
+ for i in range(len(options.device_id)):
+ device_id = options.device_id[i]
+ device_pin = device_pins[i] if i < len(device_pins) else None
+ options.devices.append(Device(device_id, device_pin))
+ del options.device_id
- paths = [
- path for path in [options.apk, options.apks, options.bundle]
- if path is not None]
- assert len(paths) <= 1, 'Expected at most one .apk, .apks, or .aab file.'
- assert options.main_activity is not None or options.use_existing_profile, \
- 'Argument --main-activity is required except when running with ' \
- '--use-existing-profile.'
+ paths = [
+ path for path in [options.apk, options.apks, options.bundle]
+ if path is not None
+ ]
+ assert len(paths) <= 1, 'Expected at most one .apk, .apks, or .aab file.'
+ assert options.main_activity is not None or options.use_existing_profile, \
+ 'Argument --main-activity is required except when running with ' \
+ '--use-existing-profile.'
- return options, args
+ return options, args
+
def run_on_device(device, options, startup_descriptors):
- adb_utils.root(device.device_id)
- if options.apk:
- adb_utils.uninstall(options.app_id, device.device_id)
- adb_utils.install(options.apk, device.device_id)
- elif options.apks:
- adb_utils.uninstall(options.app_id, device.device_id)
- adb_utils.install_apks(options.apks, device.device_id)
- elif options.bundle:
- adb_utils.uninstall(options.app_id, device.device_id)
- adb_utils.install_bundle(options.bundle, device.device_id)
- # Grant notifications.
- if options.grant_post_notification_permission:
- adb_utils.grant(
- options.app_id,
- 'android.permission.POST_NOTIFICATIONS',
- device.device_id)
- if options.until_stable:
- iteration = 0
- stable_iterations = 0
- while True:
- old_startup_descriptors = startup_descriptors
- startup_descriptors = extend_startup_descriptors(
- old_startup_descriptors, iteration, device, options)
- diff = len(startup_descriptors) - len(old_startup_descriptors)
- if diff == 0:
- stable_iterations = stable_iterations + 1
- if stable_iterations == options.until_stable_iterations:
- break
- else:
+ adb_utils.root(device.device_id)
+ if options.apk:
+ adb_utils.uninstall(options.app_id, device.device_id)
+ adb_utils.install(options.apk, device.device_id)
+ elif options.apks:
+ adb_utils.uninstall(options.app_id, device.device_id)
+ adb_utils.install_apks(options.apks, device.device_id)
+ elif options.bundle:
+ adb_utils.uninstall(options.app_id, device.device_id)
+ adb_utils.install_bundle(options.bundle, device.device_id)
+ # Grant notifications.
+ if options.grant_post_notification_permission:
+ adb_utils.grant(options.app_id, 'android.permission.POST_NOTIFICATIONS',
+ device.device_id)
+ if options.until_stable:
+ iteration = 0
stable_iterations = 0
- iteration = iteration + 1
- else:
- for iteration in range(options.iterations):
- startup_descriptors = extend_startup_descriptors(
- startup_descriptors, iteration, device, options)
- return startup_descriptors
+ while True:
+ old_startup_descriptors = startup_descriptors
+ startup_descriptors = extend_startup_descriptors(
+ old_startup_descriptors, iteration, device, options)
+ diff = len(startup_descriptors) - len(old_startup_descriptors)
+ if diff == 0:
+ stable_iterations = stable_iterations + 1
+ if stable_iterations == options.until_stable_iterations:
+ break
+ else:
+ stable_iterations = 0
+ iteration = iteration + 1
+ else:
+ for iteration in range(options.iterations):
+ startup_descriptors = extend_startup_descriptors(
+ startup_descriptors, iteration, device, options)
+ return startup_descriptors
+
def main(argv):
- (options, args) = parse_options(argv)
- startup_descriptors = {}
- for device in options.devices:
- startup_descriptors = run_on_device(device, options, startup_descriptors)
- if options.out is not None:
- with open(options.out, 'w') as f:
- for startup_descriptor, flags in startup_descriptors.items():
- if should_include_startup_descriptor(startup_descriptor, flags, options):
- f.write(startup_descriptor_to_string(startup_descriptor, flags))
- f.write('\n')
- else:
- for startup_descriptor, flags in startup_descriptors.items():
- if should_include_startup_descriptor(startup_descriptor, flags, options):
- print(startup_descriptor_to_string(startup_descriptor, flags))
+ (options, args) = parse_options(argv)
+ startup_descriptors = {}
+ for device in options.devices:
+ startup_descriptors = run_on_device(device, options,
+ startup_descriptors)
+ if options.out is not None:
+ with open(options.out, 'w') as f:
+ for startup_descriptor, flags in startup_descriptors.items():
+ if should_include_startup_descriptor(startup_descriptor, flags,
+ options):
+ f.write(
+ startup_descriptor_to_string(startup_descriptor, flags))
+ f.write('\n')
+ else:
+ for startup_descriptor, flags in startup_descriptors.items():
+ if should_include_startup_descriptor(startup_descriptor, flags,
+ options):
+ print(startup_descriptor_to_string(startup_descriptor, flags))
+
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))