-- -- Copyright 2019 The Android Open Source Project -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- https://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- Create the base tables and views containing the launch spans. SELECT RUN_METRIC('android/android_startup_launches.sql'); SELECT RUN_METRIC('android/process_metadata.sql'); SELECT RUN_METRIC('android/hsc_startups.sql'); -- Create the base CPU span join table. SELECT RUN_METRIC('android/android_cpu_agg.sql'); -- Create a span join safe launches view; since both views -- being span joined have an "id" column, we need to rename -- the id column for launches to disambiguate the two. DROP VIEW IF EXISTS launches_span_join_safe; CREATE VIEW launches_span_join_safe AS SELECT ts, dur, id AS launch_id FROM launches; -- Span join the CPU table with the launches table to get the -- breakdown per-cpu. DROP TABLE IF EXISTS cpu_freq_sched_per_thread_per_launch; CREATE VIRTUAL TABLE cpu_freq_sched_per_thread_per_launch USING SPAN_JOIN( launches_span_join_safe, cpu_freq_sched_per_thread PARTITIONED cpu ); SELECT RUN_METRIC('android/cpu_info.sql'); DROP VIEW IF EXISTS mcycles_per_core_type_per_launch; CREATE VIEW mcycles_per_core_type_per_launch AS SELECT launch_id, IFNULL(core_type_per_cpu.core_type, 'unknown') AS core_type, CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) AS mcycles FROM cpu_freq_sched_per_thread_per_launch LEFT JOIN core_type_per_cpu USING (cpu) WHERE utid != 0 GROUP BY 1, 2; -- Slices for forked processes. Never present in hot starts. -- Prefer this over process start_ts, since the process might have -- been preforked. DROP VIEW IF EXISTS zygote_fork_slice; CREATE VIEW zygote_fork_slice AS SELECT slice.ts, slice.dur, STR_SPLIT(slice.name, ": ", 1) AS process_name FROM slice WHERE name LIKE 'Start proc: %'; DROP TABLE IF EXISTS zygote_forks_by_id; CREATE TABLE zygote_forks_by_id AS SELECT launches.id, zygote_fork_slice.ts, zygote_fork_slice.dur FROM zygote_fork_slice JOIN launches ON (launches.ts < zygote_fork_slice.ts AND zygote_fork_slice.ts + zygote_fork_slice.dur < launches.ts_end AND zygote_fork_slice.process_name = launches.package ); DROP VIEW IF EXISTS launch_main_threads; CREATE VIEW launch_main_threads AS SELECT launches.ts AS ts, launches.dur AS dur, launches.id AS launch_id, thread.utid AS utid FROM launches JOIN launch_processes ON launches.id = launch_processes.launch_id JOIN process USING(upid) JOIN thread ON (process.upid = thread.upid AND process.pid = thread.tid) ORDER BY ts; DROP VIEW IF EXISTS thread_state_extended; CREATE VIEW thread_state_extended AS SELECT ts, IIF(dur = -1, (SELECT end_ts FROM trace_bounds), dur) AS dur, utid, state FROM thread_state; DROP TABLE IF EXISTS main_thread_state; CREATE VIRTUAL TABLE main_thread_state USING SPAN_JOIN( launch_main_threads PARTITIONED utid, thread_state_extended PARTITIONED utid); DROP VIEW IF EXISTS launch_by_thread_state; CREATE VIEW launch_by_thread_state AS SELECT launch_id, state, SUM(dur) AS dur FROM main_thread_state GROUP BY 1, 2; -- Tracks all main thread process threads. DROP VIEW IF EXISTS launch_threads; CREATE VIEW launch_threads AS SELECT launches.id AS launch_id, launches.ts AS ts, launches.dur AS dur, thread.utid AS utid, thread.name AS thread_name FROM launches JOIN launch_processes ON (launches.id = launch_processes.launch_id) JOIN thread ON (launch_processes.upid = thread.upid); -- Tracks all slices for the main process threads DROP VIEW IF EXISTS main_process_slice_unaggregated; CREATE VIEW main_process_slice_unaggregated AS SELECT launch_threads.launch_id AS launch_id, launch_threads.utid AS utid, launch_threads.thread_name AS thread_name, slice.name AS slice_name, slice.ts AS slice_ts, slice.dur AS slice_dur FROM launch_threads JOIN thread_track USING (utid) JOIN slice ON ( slice.track_id = thread_track.id AND slice.ts BETWEEN launch_threads.ts AND launch_threads.ts + launch_threads.dur) WHERE slice.name IN ( 'PostFork', 'ActivityThreadMain', 'bindApplication', 'activityStart', 'activityRestart', 'activityResume', 'inflate', 'ResourcesManager#getResources', 'binder transaction') OR slice.name LIKE 'performResume:%' OR slice.name LIKE 'performCreate:%' OR slice.name LIKE 'location=% status=% filter=% reason=%' OR slice.name LIKE 'OpenDexFilesFromOat%' OR slice.name LIKE 'VerifyClass%' OR slice.name LIKE 'Choreographer#doFrame%' OR slice.name LIKE 'JIT compiling%' OR slice.name LIKE '%mark sweep GC' OR slice.name LIKE '%concurrent copying GC' OR slice.name LIKE '%semispace GC'; DROP TABLE IF EXISTS main_process_slice; CREATE TABLE main_process_slice AS SELECT launch_id, CASE WHEN slice_name LIKE 'OpenDexFilesFromOat%' THEN 'OpenDexFilesFromOat' WHEN slice_name LIKE 'VerifyClass%' THEN 'VerifyClass' WHEN slice_name LIKE 'JIT compiling%' THEN 'JIT compiling' WHEN slice_name LIKE '%mark sweep GC' THEN 'GC' WHEN slice_name LIKE '%concurrent copying GC' THEN 'GC' WHEN slice_name LIKE '%semispace GC' THEN 'GC' ELSE slice_name END AS name, AndroidStartupMetric_Slice( 'dur_ns', SUM(slice_dur), 'dur_ms', SUM(slice_dur) / 1e6 ) AS slice_proto FROM main_process_slice_unaggregated GROUP BY 1, 2; DROP TABLE IF EXISTS report_fully_drawn_per_launch; CREATE TABLE report_fully_drawn_per_launch AS WITH report_fully_drawn_launch_slices AS ( SELECT launches.id AS launch_id, launches.ts AS launch_ts, min(slice.ts) as report_fully_drawn_ts FROM launches JOIN launch_processes ON (launches.id = launch_processes.launch_id) JOIN thread ON (launch_processes.upid = thread.upid) JOIN thread_track USING (utid) JOIN slice ON ( slice.track_id = thread_track.id AND slice.ts >= launches.ts) WHERE slice.name LIKE 'reportFullyDrawn%' GROUP BY launches.id ) SELECT launch_id, report_fully_drawn_ts - launch_ts as report_fully_drawn_dur FROM report_fully_drawn_launch_slices; DROP VIEW IF EXISTS to_event_protos; CREATE VIEW to_event_protos AS SELECT slice.name as slice_name, launch_id, AndroidStartupMetric_Slice( 'dur_ns', slice.ts - l.ts, 'dur_ms', (slice.ts - l.ts) / 1e6 ) as slice_proto FROM launch_main_threads l JOIN thread_track USING (utid) JOIN slice ON ( slice.track_id = thread_track.id AND slice.ts BETWEEN l.ts AND l.ts + l.dur); DROP VIEW IF EXISTS gc_slices; CREATE VIEW gc_slices AS SELECT slice_ts AS ts, slice_dur AS dur, utid, launch_id FROM main_process_slice_unaggregated WHERE ( slice_name LIKE '%mark sweep GC' OR slice_name LIKE '%concurrent copying GC' OR slice_name LIKE '%semispace GC'); DROP TABLE IF EXISTS gc_slices_by_state; CREATE VIRTUAL TABLE gc_slices_by_state USING SPAN_JOIN(gc_slices PARTITIONED utid, thread_state_extended PARTITIONED utid); DROP TABLE IF EXISTS gc_slices_by_state_materialized; CREATE TABLE gc_slices_by_state_materialized AS SELECT launch_id, SUM(dur) as sum_dur FROM gc_slices_by_state WHERE state = 'Running' GROUP BY launch_id; DROP TABLE IF EXISTS launch_threads_cpu; CREATE VIRTUAL TABLE launch_threads_cpu USING SPAN_JOIN(launch_threads PARTITIONED utid, thread_state_extended PARTITIONED utid); DROP TABLE IF EXISTS launch_threads_cpu_materialized; CREATE TABLE launch_threads_cpu_materialized AS SELECT launch_id, SUM(dur) as sum_dur FROM launch_threads_cpu WHERE thread_name = 'Jit thread pool' AND state = 'Running' GROUP BY launch_id; DROP TABLE IF EXISTS activity_names_materialized; CREATE TABLE activity_names_materialized AS SELECT launch_id, slice_name, slice_ts FROM main_process_slice_unaggregated WHERE (slice_name LIKE 'performResume:%' OR slice_name LIKE 'performCreate:%'); DROP TABLE IF EXISTS jit_compiled_methods_materialized; CREATE TABLE jit_compiled_methods_materialized AS SELECT launch_id, COUNT(1) as count FROM main_process_slice_unaggregated WHERE slice_name LIKE 'JIT compiling%' AND thread_name = 'Jit thread pool' GROUP BY launch_id; DROP TABLE IF EXISTS long_binder_transactions; CREATE TABLE long_binder_transactions AS SELECT launch_id, slice_dur, thread_name FROM main_process_slice_unaggregated WHERE slice_name = 'binder transaction' AND slice_dur >= 1e8; DROP VIEW IF EXISTS startup_view; CREATE VIEW startup_view AS SELECT AndroidStartupMetric_Startup( 'startup_id', launches.id, 'package_name', launches.package, 'process_name', ( SELECT p.name FROM launch_processes lp JOIN process p USING (upid) WHERE lp.launch_id = launches.id LIMIT 1 ), 'process', ( SELECT m.metadata FROM process_metadata m JOIN launch_processes p USING (upid) WHERE p.launch_id = launches.id LIMIT 1 ), 'activities', ( SELECT RepeatedField(AndroidStartupMetric_Activity( 'name', (SELECT STR_SPLIT(s.slice_name, ':', 1)), 'method', (SELECT STR_SPLIT(s.slice_name, ':', 0)), 'ts_method_start', s.slice_ts )) FROM activity_names_materialized s WHERE s.launch_id = launches.id ), 'long_binder_transactions', ( SELECT RepeatedField(AndroidStartupMetric_BinderTransaction( 'duration', AndroidStartupMetric_Slice( 'dur_ns', lbt.slice_dur, 'dur_ms', lbt.slice_dur / 1e6 ), 'thread', lbt.thread_name )) FROM long_binder_transactions lbt WHERE lbt.launch_id = launches.id ), 'zygote_new_process', EXISTS(SELECT TRUE FROM zygote_forks_by_id WHERE id = launches.id), 'activity_hosting_process_count', ( SELECT COUNT(1) FROM launch_processes p WHERE p.launch_id = launches.id ), 'event_timestamps', AndroidStartupMetric_EventTimestamps( 'intent_received', launches.ts, 'first_frame', launches.ts_end ), 'to_first_frame', AndroidStartupMetric_ToFirstFrame( 'dur_ns', launches.dur, 'dur_ms', launches.dur / 1e6, 'main_thread_by_task_state', AndroidStartupMetric_TaskStateBreakdown( 'running_dur_ns', IFNULL( ( SELECT dur FROM launch_by_thread_state l WHERE l.launch_id = launches.id AND state = 'Running' ), 0), 'runnable_dur_ns', IFNULL( ( SELECT dur FROM launch_by_thread_state l WHERE l.launch_id = launches.id AND state = 'R' ), 0), 'uninterruptible_sleep_dur_ns', IFNULL( ( SELECT dur FROM launch_by_thread_state l WHERE l.launch_id = launches.id AND (state = 'D' or state = 'DK') ), 0), 'interruptible_sleep_dur_ns', IFNULL( ( SELECT dur FROM launch_by_thread_state l WHERE l.launch_id = launches.id AND state = 'S' ), 0) ), 'mcycles_by_core_type', AndroidStartupMetric_McyclesByCoreType( 'little', ( SELECT mcycles FROM mcycles_per_core_type_per_launch m WHERE m.launch_id = launches.id AND m.core_type = 'little' ), 'big', ( SELECT mcycles FROM mcycles_per_core_type_per_launch m WHERE m.launch_id = launches.id AND m.core_type = 'big' ), 'bigger', ( SELECT mcycles FROM mcycles_per_core_type_per_launch m WHERE m.launch_id = launches.id AND m.core_type = 'bigger' ), 'unknown', ( SELECT mcycles FROM mcycles_per_core_type_per_launch m WHERE m.launch_id = launches.id AND m.core_type = 'unknown' ) ), 'to_post_fork', ( SELECT slice_proto FROM to_event_protos p WHERE p.launch_id = launches.id AND slice_name = 'PostFork' ), 'to_activity_thread_main', ( SELECT slice_proto FROM to_event_protos p WHERE p.launch_id = launches.id AND slice_name = 'ActivityThreadMain' ), 'to_bind_application', ( SELECT slice_proto FROM to_event_protos p WHERE p.launch_id = launches.id AND slice_name = 'bindApplication' ), 'other_processes_spawned_count', ( SELECT COUNT(1) FROM process WHERE (process.name IS NULL OR process.name != launches.package) AND process.start_ts BETWEEN launches.ts AND launches.ts + launches.dur ), 'time_activity_manager', ( SELECT AndroidStartupMetric_Slice( 'dur_ns', l.ts - launches.ts, 'dur_ms', (l.ts - launches.ts) / 1e6 ) FROM launching_events l WHERE l.ts BETWEEN launches.ts AND launches.ts + launches.dur ), 'time_post_fork', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'PostFork' ), 'time_activity_thread_main', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'ActivityThreadMain' ), 'time_bind_application', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'bindApplication' ), 'time_activity_start', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'activityStart' ), 'time_activity_resume', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'activityResume' ), 'time_activity_restart', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'activityRestart' ), 'time_choreographer', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name LIKE 'Choreographer#doFrame%' ), 'time_before_start_process', ( SELECT AndroidStartupMetric_Slice( 'dur_ns', ts - launches.ts, 'dur_ms', (ts - launches.ts) / 1e6 ) FROM zygote_forks_by_id z WHERE z.id = launches.id ), 'time_during_start_process', ( SELECT AndroidStartupMetric_Slice( 'dur_ns', dur, 'dur_ms', dur / 1e6 ) FROM zygote_forks_by_id z WHERE z.id = launches.id ), 'time_inflate', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'inflate' ), 'time_get_resources', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'ResourcesManager#getResources' ), 'time_dex_open', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'OpenDexFilesFromOat' ), 'time_verify_class', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'VerifyClass' ), 'jit_compiled_methods', ( SELECT count FROM jit_compiled_methods_materialized s WHERE s.launch_id = launches.id ), 'time_jit_thread_pool_on_cpu', ( SELECT NULL_IF_EMPTY(AndroidStartupMetric_Slice( 'dur_ns', sum_dur, 'dur_ms', sum_dur / 1e6 )) FROM launch_threads_cpu_materialized WHERE launch_id = launches.id ), 'time_gc_total', ( SELECT slice_proto FROM main_process_slice s WHERE s.launch_id = launches.id AND name = 'GC' ), 'time_gc_on_cpu', ( SELECT NULL_IF_EMPTY(AndroidStartupMetric_Slice( 'dur_ns', sum_dur, 'dur_ms', sum_dur / 1e6 )) FROM gc_slices_by_state_materialized WHERE launch_id = launches.id ) ), 'hsc', ( SELECT NULL_IF_EMPTY(AndroidStartupMetric_HscMetrics( 'full_startup', ( SELECT AndroidStartupMetric_Slice( 'dur_ns', h.ts_total, 'dur_ms', h.ts_total / 1e6 ) FROM hsc_based_startup_times h WHERE h.id = launches.id ) )) ), 'report_fully_drawn', ( SELECT NULL_IF_EMPTY(AndroidStartupMetric_Slice( 'dur_ns', report_fully_drawn_dur, 'dur_ms', report_fully_drawn_dur / 1e6 )) FROM report_fully_drawn_per_launch r WHERE r.launch_id = launches.id ), 'optimization_status',( SELECT RepeatedField(AndroidStartupMetric_OptimizationStatus( 'location', SUBSTR(STR_SPLIT(name, ' status=', 0), LENGTH('location=') + 1), 'odex_status', STR_SPLIT(STR_SPLIT(name, ' status=', 1), ' filter=', 0), 'compilation_filter', STR_SPLIT(STR_SPLIT(name, ' filter=', 1), ' reason=', 0), 'compilation_reason', STR_SPLIT(name, ' reason=', 1) )) FROM main_process_slice s WHERE name LIKE 'location=% status=% filter=% reason=%' ) ) as startup FROM launches; DROP VIEW IF EXISTS android_startup_event; CREATE VIEW android_startup_event AS SELECT 'slice' as track_type, 'Android App Startups' as track_name, l.ts as ts, l.dur as dur, l.package as slice_name FROM launches l; DROP VIEW IF EXISTS android_startup_output; CREATE VIEW android_startup_output AS SELECT AndroidStartupMetric( 'startup', ( SELECT RepeatedField(startup) FROM startup_view ) );