Include startup time according to adb start-activity
Change-Id: I9d967848492457d266b3346e86b096a9303aefe7
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 41717cf..136c99a 100644
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -180,13 +180,25 @@
assert len(stdout) == 0
time.sleep(sleep_in_seconds)
-def launch_activity(app_id, activity, device_id=None):
- cmd = create_adb_cmd(
- 'shell am start -n %s/%s' % (app_id, activity), device_id)
+def launch_activity(
+ app_id, activity, device_id=None, wait_for_activity_to_launch=False):
+ args = ['shell', 'am', 'start', '-n', '%s/%s' % (app_id, activity)]
+ if wait_for_activity_to_launch:
+ args.append('-W')
+ cmd = create_adb_cmd(args, device_id)
stdout = subprocess.check_output(cmd).decode('utf-8').strip()
expected_stdout = (
'Starting: Intent { cmp=%s/.%s }' % (app_id, activity[len(app_id)+1:]))
- assert stdout == expected_stdout, 'was %s' % stdout
+ assert stdout.startswith(expected_stdout), 'was %s' % stdout
+ lines = stdout.splitlines()
+ result = {}
+ for line in lines:
+ if line.startswith('TotalTime: '):
+ total_time_str = line.removeprefix('TotalTime: ')
+ assert total_time_str.isdigit()
+ result['total_time'] = int(total_time_str)
+ assert not wait_for_activity_to_launch or 'total_time' in result
+ return result
def prepare_for_interaction_with_device(device_id=None, device_pin=None):
# Increase screen off timeout to avoid device screen turns off.
diff --git a/tools/startup/trace_generator.py b/tools/startup/trace_generator.py
index b4a4089..37f5c71 100755
--- a/tools/startup/trace_generator.py
+++ b/tools/startup/trace_generator.py
@@ -89,14 +89,17 @@
out_dir, tmp_dir)
# Launch main activity.
- adb_utils.launch_activity(
- options.app_id, options.main_activity, options.device_id)
+ launch_activity_result = adb_utils.launch_activity(
+ options.app_id,
+ options.main_activity,
+ options.device_id,
+ wait_for_activity_to_launch=True)
# Wait for perfetto trace collector to stop.
perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
# Get minor and major page faults from app process.
- data = compute_data(perfetto_trace_path, options)
+ data = compute_data(launch_activity_result, perfetto_trace_path, options)
write_data(out_dir, data)
return data
@@ -104,7 +107,7 @@
for key, value in data.items():
if key == 'time':
continue
- if hasattr(sum_data, key):
+ if key in sum_data:
if key == 'app_id':
assert sum_data[key] == value
else:
@@ -115,7 +118,7 @@
else:
sum_data[key] = value
-def compute_data(perfetto_trace_path, options):
+def compute_data(launch_activity_result, perfetto_trace_path, options):
minfl, majfl = adb_utils.get_minor_major_page_faults(
options.app_id, options.device_id)
data = {
@@ -124,36 +127,45 @@
'minfl': minfl,
'majfl': majfl
}
- startup_data = compute_startup_data(perfetto_trace_path, options)
+ startup_data = compute_startup_data(
+ launch_activity_result, perfetto_trace_path, options)
return data | startup_data
-def compute_startup_data(perfetto_trace_path, options):
- trace_processor = TraceProcessor(file_path=perfetto_trace_path)
-
- # Compute time to first frame according to the builtin android_startup metric.
- startup_metric = trace_processor.metric(['android_startup'])
- time_to_first_frame_ms = \
- startup_metric.android_startup.startup[0].to_first_frame.dur_ms
-
- # Compute time to first and last doFrame event.
- bind_application_slice = perfetto_utils.find_unique_slice_by_name(
- 'bindApplication', options, trace_processor)
- activity_start_slice = perfetto_utils.find_unique_slice_by_name(
- 'activityStart', options, trace_processor)
- do_frame_slices = perfetto_utils.find_slices_by_name(
- 'Choreographer#doFrame', options, trace_processor)
- first_do_frame_slice = next(do_frame_slices)
- *_, last_do_frame_slice = do_frame_slices
-
- return {
- 'time_to_first_frame_ms': time_to_first_frame_ms,
- 'time_to_first_choreographer_do_frame_ms':
- perfetto_utils.get_slice_end_since_start(
- first_do_frame_slice, bind_application_slice),
- 'time_to_last_choreographer_do_frame_ms':
- perfetto_utils.get_slice_end_since_start(
- last_do_frame_slice, bind_application_slice)
+def compute_startup_data(launch_activity_result, perfetto_trace_path, options):
+ startup_data = {
+ 'time_to_activity_started_ms': launch_activity_result.get('total_time')
}
+ perfetto_startup_data = {}
+ if not options.no_perfetto:
+ trace_processor = TraceProcessor(file_path=perfetto_trace_path)
+
+ # Compute time to first frame according to the builtin android_startup metric.
+ startup_metric = trace_processor.metric(['android_startup'])
+ time_to_first_frame_ms = \
+ startup_metric.android_startup.startup[0].to_first_frame.dur_ms
+
+ # Compute time to first and last doFrame event.
+ bind_application_slice = perfetto_utils.find_unique_slice_by_name(
+ 'bindApplication', options, trace_processor)
+ activity_start_slice = perfetto_utils.find_unique_slice_by_name(
+ 'activityStart', options, trace_processor)
+ do_frame_slices = perfetto_utils.find_slices_by_name(
+ 'Choreographer#doFrame', options, trace_processor)
+ first_do_frame_slice = next(do_frame_slices)
+ *_, last_do_frame_slice = do_frame_slices
+
+ perfetto_startup_data = {
+ 'time_to_first_frame_ms': round(time_to_first_frame_ms),
+ 'time_to_first_choreographer_do_frame_ms':
+ round(perfetto_utils.get_slice_end_since_start(
+ first_do_frame_slice, bind_application_slice)),
+ 'time_to_last_choreographer_do_frame_ms':
+ round(perfetto_utils.get_slice_end_since_start(
+ last_do_frame_slice, bind_application_slice))
+ }
+
+ # Return combined startup data.
+ return startup_data | perfetto_startup_data
def write_data(out_dir, data):
data_path = os.path.join(out_dir, 'data.txt')
@@ -193,6 +205,10 @@
result.add_argument('--main-activity',
help='Main activity class name',
required=True)
+ result.add_argument('--no-perfetto',
+ help='Disables perfetto trace generation',
+ action='store_true',
+ default=False)
result.add_argument('--out-dir',
help='Directory to store trace files in',
required=True)