tp: rewrite startup metric to be much easier to understand and extend
This CL overhauls the whole startup metric to be oriented around
functions which allows much better composability than having big
monolithic views.
It also makes debugging much easier as errors in functions will be
scoped to just this function.
There should be no behavioural change as a result of this CL.
Bug: 234546650
Bug: 190219056
Change-Id: Ifc747d11d6a58199956b1cfef7d7e2c8ba9a2dc0
diff --git a/Android.bp b/Android.bp
index 7378bad..4cb171f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8594,11 +8594,15 @@
"src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql",
"src/trace_processor/metrics/sql/android/profiler_smaps.sql",
"src/trace_processor/metrics/sql/android/span_view_stats.sql",
+ "src/trace_processor/metrics/sql/android/startup/gc_slices.sql",
"src/trace_processor/metrics/sql/android/startup/hsc.sql",
"src/trace_processor/metrics/sql/android/startup/launches.sql",
"src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql",
"src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql",
"src/trace_processor/metrics/sql/android/startup/launches_minsdk33.sql",
+ "src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql",
+ "src/trace_processor/metrics/sql/android/startup/slice_functions.sql",
+ "src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql",
"src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
"src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
"src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
diff --git a/BUILD b/BUILD
index 7d2bfaf..608dd87 100644
--- a/BUILD
+++ b/BUILD
@@ -1190,11 +1190,15 @@
"src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql",
"src/trace_processor/metrics/sql/android/profiler_smaps.sql",
"src/trace_processor/metrics/sql/android/span_view_stats.sql",
+ "src/trace_processor/metrics/sql/android/startup/gc_slices.sql",
"src/trace_processor/metrics/sql/android/startup/hsc.sql",
"src/trace_processor/metrics/sql/android/startup/launches.sql",
"src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql",
"src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql",
"src/trace_processor/metrics/sql/android/startup/launches_minsdk33.sql",
+ "src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql",
+ "src/trace_processor/metrics/sql/android/startup/slice_functions.sql",
+ "src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql",
"src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
"src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
"src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index 5a2cae7..faadbdd 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -84,6 +84,10 @@
"android/startup/launches_minsdk33.sql",
"android/startup/launches.sql",
"android/startup/hsc.sql",
+ "android/startup/gc_slices.sql",
+ "android/startup/mcycles_per_launch.sql",
+ "android/startup/slice_functions.sql",
+ "android/startup/thread_state_breakdown.sql",
"chrome/actual_power_by_category.sql",
"chrome/actual_power_by_rail_mode.sql",
"chrome/chrome_event_metadata.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_frame_timeline_metric.sql b/src/trace_processor/metrics/sql/android/android_frame_timeline_metric.sql
index 72ef920..f290839 100644
--- a/src/trace_processor/metrics/sql/android/android_frame_timeline_metric.sql
+++ b/src/trace_processor/metrics/sql/android/android_frame_timeline_metric.sql
@@ -21,7 +21,7 @@
FROM actual_frame_timeline_slice
LEFT JOIN process AS p
USING (upid)
-WHERE jank_type LIKE '%App Deadline Missed%'
+WHERE jank_type GLOB '*App Deadline Missed*'
GROUP BY process;
DROP VIEW IF EXISTS android_frame_timeline_metric_output;
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index fda4553..e6556fb 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -16,285 +16,96 @@
-- Create the base tables and views containing the launch spans.
SELECT RUN_METRIC('android/startup/launches.sql');
+
+-- Define the helper functions which will be used throught the remainder
+-- of the metric.
+SELECT RUN_METRIC('android/startup/slice_functions.sql');
+
+-- Run all the HSC metrics.
SELECT RUN_METRIC('android/startup/hsc.sql');
+
+-- Define some helper functions related to breaking down thread state
+-- for launches.
+SELECT RUN_METRIC('android/startup/thread_state_breakdown.sql');
+
+-- Define helper functions to break down slices/threads by thread
+-- state.
+SELECT RUN_METRIC('android/startup/mcycles_per_launch.sql');
+
+-- Define helper functions for GC slices.
+SELECT RUN_METRIC('android/startup/gc_slices.sql');
+
+-- Define process metadata functions.
SELECT RUN_METRIC('android/process_metadata.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.
+-- Returns the 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 GLOB '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
+SELECT CREATE_VIEW_FUNCTION(
+ 'ZYGOTE_FORK_FOR_LAUNCH(launch_id INT)',
+ 'ts INT, dur INT',
+ '
+ SELECT slice.ts, slice.dur
+ FROM launches
+ JOIN slice ON (
+ launches.ts < slice.ts AND
+ slice.ts + slice.dur < launches.ts_end AND
+ STR_SPLIT(slice.name, ": ", 1) = launches.package
+ )
+ WHERE launches.id = $launch_id AND slice.name GLOB "Start proc: *"
+ '
);
-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.id AS slice_id,
- slice.arg_set_id AS arg_set_id,
- 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 GLOB 'performResume:*'
- OR slice.name GLOB 'performCreate:*'
- OR slice.name GLOB 'location=* status=* filter=* reason=*'
- OR slice.name GLOB 'OpenDexFilesFromOat*'
- OR slice.name GLOB 'VerifyClass*'
- OR slice.name GLOB 'Choreographer#doFrame*'
- OR slice.name GLOB 'JIT compiling*'
- OR slice.name GLOB '*mark sweep GC'
- OR slice.name GLOB '*concurrent copying GC'
- OR slice.name GLOB '*semispace GC';
-
-DROP TABLE IF EXISTS main_process_slice;
-CREATE TABLE main_process_slice AS
-SELECT
- launch_id,
- CASE
- WHEN slice_name GLOB 'OpenDexFilesFromOat*' THEN 'OpenDexFilesFromOat'
- WHEN slice_name GLOB 'VerifyClass*' THEN 'VerifyClass'
- WHEN slice_name GLOB 'JIT compiling*' THEN 'JIT compiling'
- WHEN slice_name GLOB '*mark sweep GC' THEN 'GC'
- WHEN slice_name GLOB '*concurrent copying GC' THEN 'GC'
- WHEN slice_name GLOB '*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 GLOB '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 GLOB '*mark sweep GC'
- OR slice_name GLOB '*concurrent copying GC'
- OR slice_name GLOB '*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 GLOB 'performResume:*' OR slice_name GLOB '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 GLOB '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
- s.slice_id,
- s.launch_id,
- s.slice_dur,
- s.thread_name,
- EXTRACT_ARG(s.arg_set_id, 'destination name') AS destination_thread,
- process.name AS destination_process,
- EXTRACT_ARG(s.arg_set_id, 'flags') AS flags,
- EXTRACT_ARG(s.arg_set_id, 'code') AS code,
- EXTRACT_ARG(s.arg_set_id, 'data_size') AS data_size
-FROM
- main_process_slice_unaggregated s
-JOIN process ON (EXTRACT_ARG(s.arg_set_id, 'destination process') = process.pid)
-WHERE
- s.slice_name = 'binder transaction' AND
- s.slice_dur >= 5e7;
-
+-- Returns the fully drawn slice proto given a launch id.
SELECT CREATE_FUNCTION(
- 'MAIN_PROCESS_SLICE_PROTO(launch_id LONG, name STRING)',
- 'PROTO', '
- SELECT slice_proto
- FROM main_process_slice s
- WHERE s.launch_id = $launch_id AND name GLOB $name
- LIMIT 1
- ');
+ 'REPORT_FULLY_DRAWN_FOR_LAUNCH(launch_id INT)',
+ 'PROTO',
+ '
+ SELECT
+ STARTUP_SLICE_PROTO(report_fully_drawn_ts - launch_ts)
+ FROM (
+ SELECT
+ 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 USING (upid)
+ JOIN thread_track USING (utid)
+ JOIN slice ON (slice.track_id = thread_track.id)
+ WHERE
+ slice.name GLOB "reportFullyDrawn*" AND
+ slice.ts >= launches.ts AND
+ launches.id = $launch_id
+ )
+ '
+);
+-- Returns a repeated field of all long binder transaction protos for
+-- a given launch id.
+SELECT CREATE_FUNCTION(
+ 'BINDER_TRANSACTION_PROTO_FOR_LAUNCH(launch_id INT)',
+ 'PROTO',
+ '
+ SELECT RepeatedField(
+ AndroidStartupMetric_BinderTransaction(
+ "duration", STARTUP_SLICE_PROTO(s.slice_dur),
+ "thread", s.thread_name,
+ "destination_thread", EXTRACT_ARG(s.arg_set_id, "destination name"),
+ "destination_process", process.name,
+ "flags", EXTRACT_ARG(s.arg_set_id, "flags"),
+ "code", EXTRACT_ARG(s.arg_set_id, "code"),
+ "data_size", EXTRACT_ARG(s.arg_set_id, "data_size")
+ )
+ )
+ FROM SLICES_FOR_LAUNCH_AND_SLICE_NAME($launch_id, "binder transaction") s
+ JOIN process ON (
+ EXTRACT_ARG(s.arg_set_id, "destination process") = process.pid
+ )
+ WHERE s.slice_dur > 5e7
+ '
+);
+
+-- Define the view
DROP VIEW IF EXISTS startup_view;
CREATE VIEW startup_view AS
SELECT
@@ -321,26 +132,13 @@
'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
+ FROM thread_slices_for_all_launches s
+ WHERE
+ s.launch_id = launches.id AND
+ (s.slice_name GLOB 'performResume:*' OR s.slice_name GLOB 'performCreate:*')
),
- '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,
- 'destination_thread', lbt.destination_thread,
- 'destination_process', lbt.destination_process,
- 'flags', lbt.flags,
- 'code', lbt.code,
- 'data_size', lbt.data_size
- ))
- 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),
+ 'long_binder_transactions', BINDER_TRANSACTION_PROTO_FOR_LAUNCH(launches.id),
+ 'zygote_new_process', EXISTS(SELECT TRUE FROM ZYGOTE_FORK_FOR_LAUNCH(launches.id)),
'activity_hosting_process_count', (
SELECT COUNT(1) FROM launch_processes p
WHERE p.launch_id = launches.id
@@ -354,157 +152,123 @@
'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),
+ MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, 'Running'), 0
+ ),
'runnable_dur_ns', IFNULL(
- (
- SELECT dur FROM launch_by_thread_state l
- WHERE l.launch_id = launches.id AND state = 'R'
- ), 0),
+ MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, '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),
+ MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, 'D*'), 0
+ ),
'interruptible_sleep_dur_ns', IFNULL(
- (
- SELECT dur FROM launch_by_thread_state l
- WHERE l.launch_id = launches.id AND state = 'S'
- ), 0)
+ MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, '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'
- )
+ 'little', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'little'),
+ 'big', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'big'),
+ 'bigger', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'bigger'),
+ 'unknown', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, '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
- ),
+ 'to_post_fork',
+ LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.id, 'PostFork'),
+ 'to_activity_thread_main',
+ LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.id, 'ActivityThreadMain'),
+ 'to_bind_application',
+ LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.id, 'bindApplication'),
'time_activity_manager', (
- SELECT AndroidStartupMetric_Slice(
- 'dur_ns', l.ts - launches.ts,
- 'dur_ms', (l.ts - launches.ts) / 1e6
- )
+ SELECT STARTUP_SLICE_PROTO(l.ts - launches.ts)
FROM launching_events l
WHERE l.ts BETWEEN launches.ts AND launches.ts + launches.dur
),
- 'time_post_fork', MAIN_PROCESS_SLICE_PROTO(launches.id, 'PostFork'),
- 'time_activity_thread_main', MAIN_PROCESS_SLICE_PROTO(launches.id, 'ActivityThreadMain'),
- 'time_bind_application', MAIN_PROCESS_SLICE_PROTO(launches.id, 'bindApplication'),
- 'time_activity_start', MAIN_PROCESS_SLICE_PROTO(launches.id, 'activityStart'),
- 'time_activity_resume', MAIN_PROCESS_SLICE_PROTO(launches.id, 'activityResume'),
- 'time_activity_restart', MAIN_PROCESS_SLICE_PROTO(launches.id, 'activityRestart'),
- 'time_choreographer', MAIN_PROCESS_SLICE_PROTO(launches.id, 'Choreographer#doFrame*'),
- 'time_inflate', MAIN_PROCESS_SLICE_PROTO(launches.id, 'inflate'),
- 'time_get_resources', MAIN_PROCESS_SLICE_PROTO(launches.id, 'ResourcesManager#getResources'),
- 'time_dex_open', MAIN_PROCESS_SLICE_PROTO(launches.id, 'OpenDexFilesFromOat'),
- 'time_verify_class', MAIN_PROCESS_SLICE_PROTO(launches.id, 'VerifyClass'),
- 'time_gc_total', MAIN_PROCESS_SLICE_PROTO(launches.id, 'GC'),
+ 'time_post_fork',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'PostFork'),
+ 'time_activity_thread_main',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'ActivityThreadMain'),
+ 'time_bind_application',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'bindApplication'),
+ 'time_activity_start',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'activityStart'),
+ 'time_activity_resume',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'activityResume'),
+ 'time_activity_restart',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'activityRestart'),
+ 'time_choreographer',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'Choreographer#doFrame*'),
+ 'time_inflate',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'inflate'),
+ 'time_get_resources',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'ResourcesManager#getResources'),
+ 'time_dex_open',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'OpenDexFilesFromOat*'),
+ 'time_verify_class',
+ DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'VerifyClass*'),
+ 'time_gc_total', (
+ SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(SUM(slice_dur)))
+ FROM thread_slices_for_all_launches slice
+ WHERE
+ launch_id = launches.id AND
+ (
+ slice_name GLOB "*semispace GC" OR
+ slice_name GLOB "*mark sweep GC" OR
+ slice_name GLOB "*concurrent copying GC"
+ )
+ ),
'time_before_start_process', (
- SELECT AndroidStartupMetric_Slice(
- 'dur_ns', ts - launches.ts,
- 'dur_ms', (ts - launches.ts) / 1e6
+ SELECT STARTUP_SLICE_PROTO(ts - launches.ts)
+ FROM ZYGOTE_FORK_FOR_LAUNCH(launches.id)
+ ),
+ 'time_jit_thread_pool_on_cpu', NULL_IF_EMPTY(STARTUP_SLICE_PROTO(
+ THREAD_TIME_FOR_LAUNCH_STATE_AND_THREAD(
+ launches.id,
+ 'Running',
+ 'Jit thread pool'
)
- FROM zygote_forks_by_id z
- WHERE z.id = launches.id
+ )),
+ 'time_gc_on_cpu', (
+ SELECT STARTUP_SLICE_PROTO(sum_dur)
+ FROM running_gc_slices_materialized
+ WHERE launches.id = launch_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
+ SELECT STARTUP_SLICE_PROTO(dur)
+ FROM ZYGOTE_FORK_FOR_LAUNCH(launches.id)
),
'jit_compiled_methods', (
- SELECT count
- FROM jit_compiled_methods_materialized s
- WHERE s.launch_id = launches.id
+ SELECT IIF(COUNT(1) = 0, NULL, COUNT(1))
+ FROM SLICES_FOR_LAUNCH_AND_SLICE_NAME(launches.id, 'JIT compiling*')
+ WHERE thread_name = 'Jit thread pool'
),
- '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_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
+ '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
)
),
- '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',(
+ 'hsc', NULL_IF_EMPTY(AndroidStartupMetric_HscMetrics(
+ 'full_startup', (
+ SELECT STARTUP_SLICE_PROTO(h.ts_total)
+ FROM hsc_based_startup_times h
+ WHERE h.id = launches.id
+ )
+ )),
+ 'report_fully_drawn', NULL_IF_EMPTY(REPORT_FULLY_DRAWN_FOR_LAUNCH(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)
+ 'location', SUBSTR(STR_SPLIT(slice_name, ' status=', 0), LENGTH('location=') + 1),
+ 'odex_status', STR_SPLIT(STR_SPLIT(slice_name, ' status=', 1), ' filter=', 0),
+ 'compilation_filter', STR_SPLIT(STR_SPLIT(slice_name, ' filter=', 1), ' reason=', 0),
+ 'compilation_reason', STR_SPLIT(slice_name, ' reason=', 1)
))
- FROM main_process_slice s
- WHERE name GLOB 'location=* status=* filter=* reason=*'
+ FROM (
+ SELECT *
+ FROM SLICES_FOR_LAUNCH_AND_SLICE_NAME(
+ launches.id,
+ 'location=* status=* filter=* reason=*'
+ )
+ ORDER BY slice_name
+ )
)
) as startup
FROM launches;
diff --git a/src/trace_processor/metrics/sql/android/startup/gc_slices.sql b/src/trace_processor/metrics/sql/android/startup/gc_slices.sql
new file mode 100644
index 0000000..372c4da
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/startup/gc_slices.sql
@@ -0,0 +1,35 @@
+--
+-- Copyright 2022 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.
+--
+
+DROP VIEW IF EXISTS gc_slices;
+CREATE VIEW gc_slices AS
+SELECT slice_ts AS ts, slice_dur AS dur, utid, launch_id
+FROM thread_slices_for_all_launches
+WHERE
+ slice_name GLOB '*mark sweep GC' OR
+ slice_name GLOB '*concurrent copying GC' OR
+ slice_name GLOB '*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 running_gc_slices_materialized;
+CREATE TABLE running_gc_slices_materialized AS
+SELECT launch_id, SUM(dur) as sum_dur
+FROM gc_slices_by_state
+WHERE state = 'Running'
+GROUP BY launch_id;
diff --git a/src/trace_processor/metrics/sql/android/startup/launches.sql b/src/trace_processor/metrics/sql/android/startup/launches.sql
index 60c1cab..9107359 100644
--- a/src/trace_processor/metrics/sql/android/startup/launches.sql
+++ b/src/trace_processor/metrics/sql/android/startup/launches.sql
@@ -26,8 +26,9 @@
FROM slice s
JOIN process_track t ON s.track_id = t.id
JOIN process USING(upid)
-WHERE s.name GLOB 'launching: *'
-AND (process.name IS NULL OR process.name = 'system_server');
+WHERE
+ s.name GLOB 'launching: *' AND
+ (process.name IS NULL OR process.name = 'system_server');
SELECT CREATE_FUNCTION(
'SLICE_COUNT(slice_glob STRING)',
@@ -81,20 +82,40 @@
);
INSERT INTO launch_processes(launch_id, upid, launch_type)
-SELECT
- l.id AS launch_id,
- p.upid,
- CASE
- WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'bindApplication') > 0
- THEN 'cold'
- WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityStart') > 0
- THEN 'warm'
- WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityResume') > 0
- THEN 'hot'
- ELSE NULL
- END AS launch_type
-FROM launches l
-LEFT JOIN package_list ON (l.package = package_list.package_name)
-JOIN process p ON (l.package = p.name OR p.uid = package_list.uid)
-JOIN thread t ON (p.upid = t.upid AND t.is_main_thread)
+SELECT *
+FROM (
+ -- This is intentionally a nested subquery. For some reason, if we put
+ -- the `WHERE launch_type IS NOT NULL` constraint inside, we end up with a
+ -- query which is an order of magnitude slower than being outside :(
+ SELECT
+ l.id AS launch_id,
+ p.upid,
+ CASE
+ WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'bindApplication') > 0
+ THEN 'cold'
+ WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityStart') > 0
+ THEN 'warm'
+ WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityResume') > 0
+ THEN 'hot'
+ ELSE NULL
+ END AS launch_type
+ FROM launches l
+ LEFT JOIN package_list ON (l.package = package_list.package_name)
+ JOIN process p ON (l.package = p.name OR p.uid = package_list.uid)
+ JOIN thread t ON (p.upid = t.upid AND t.is_main_thread)
+)
WHERE launch_type IS NOT NULL;
+
+-- Tracks all main 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,
+ thread.is_main_thread AS is_main_thread
+FROM launches
+JOIN launch_processes ON (launches.id = launch_processes.launch_id)
+JOIN thread USING (upid);
diff --git a/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql b/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql
new file mode 100644
index 0000000..6b40e98
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql
@@ -0,0 +1,59 @@
+--
+-- Copyright 2022 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 CPU span join table.
+SELECT RUN_METRIC('android/android_cpu_agg.sql');
+SELECT RUN_METRIC('android/cpu_info.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
+);
+
+-- Materialized to avoid span-joining once per core type.
+DROP TABLE IF EXISTS mcycles_per_core_type_per_launch;
+CREATE TABLE 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;
+
+-- Given a launch id and core type, returns the number of mcycles consumed
+-- on CPUs of that core type during the launch.
+SELECT CREATE_FUNCTION(
+ 'MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launch_id INT, core_type STRING)',
+ 'INT', "
+ SELECT mcycles
+ FROM mcycles_per_core_type_per_launch m
+ WHERE m.launch_id = $launch_id AND m.core_type = $core_type
+ "
+);
diff --git a/src/trace_processor/metrics/sql/android/startup/slice_functions.sql b/src/trace_processor/metrics/sql/android/startup/slice_functions.sql
new file mode 100644
index 0000000..571bebd
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/startup/slice_functions.sql
@@ -0,0 +1,75 @@
+--
+-- Copyright 2022 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.
+--
+
+-- Helper function to build a Slice proto from a duration.
+SELECT CREATE_FUNCTION('STARTUP_SLICE_PROTO(dur INT)', 'PROTO', '
+ SELECT AndroidStartupMetric_Slice(
+ "dur_ns", $dur,
+ "dur_ms", $dur / 1e6
+ )
+');
+
+-- View containing all the slices for all launches. Generally, this view
+-- should not be used. Instead, one of the helper functions below which wrap
+-- this view should be used.
+DROP VIEW IF EXISTS thread_slices_for_all_launches;
+CREATE VIEW thread_slices_for_all_launches AS
+SELECT
+ launch_threads.ts AS launch_ts,
+ launch_threads.launch_id AS launch_id,
+ launch_threads.utid AS utid,
+ launch_threads.thread_name AS thread_name,
+ launch_threads.is_main_thread AS is_main_thread,
+ slice.arg_set_id AS arg_set_id,
+ 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)
+WHERE slice.ts BETWEEN launch_threads.ts AND launch_threads.ts + launch_threads.dur;
+
+-- Given a launch id and GLOB for a slice name, returns columns for matching slices.
+SELECT CREATE_VIEW_FUNCTION(
+ 'SLICES_FOR_LAUNCH_AND_SLICE_NAME(launch_id INT, slice_name STRING)',
+ 'slice_name STRING, slice_ts INT, slice_dur INT, thread_name STRING, arg_set_id INT', '
+ SELECT slice_name, slice_ts, slice_dur, thread_name, arg_set_id
+ FROM thread_slices_for_all_launches
+ WHERE launch_id = $launch_id AND slice_name GLOB $slice_name
+ '
+);
+
+-- Given a launch id and GLOB for a slice name, returns the startup slice proto,
+-- summing the slice durations across the whole startup.
+SELECT CREATE_FUNCTION(
+ 'DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launch_id LONG, slice_name STRING)',
+ 'PROTO', '
+ SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(SUM(slice_dur)))
+ FROM thread_slices_for_all_launches
+ WHERE launch_id = $launch_id AND slice_name GLOB $slice_name
+ '
+);
+
+-- Given a launch id and GLOB for a slice name, returns the startup slice proto by
+-- taking the duration between the start of the launch and start of the slice.
+SELECT CREATE_FUNCTION(
+ 'LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launch_id INT, slice_name STRING)',
+ 'PROTO', '
+ SELECT STARTUP_SLICE_PROTO(slice_ts - launch_ts)
+ FROM thread_slices_for_all_launches
+ WHERE slice_name GLOB $slice_name AND launch_id = $launch_id AND is_main_thread
+ '
+);
diff --git a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
new file mode 100644
index 0000000..e735aa1
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
@@ -0,0 +1,72 @@
+--
+-- Copyright 2022 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.
+--
+
+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 launch_threads_by_thread_state;
+CREATE VIRTUAL TABLE launch_threads_by_thread_state
+USING SPAN_JOIN(
+ launch_threads PARTITIONED utid,
+ thread_state_extended PARTITIONED utid
+);
+
+-- Materialized to avoid repeatedly span joining per each thread state.
+DROP TABLE IF EXISTS launch_thread_state_dur_sum;
+CREATE TABLE launch_thread_state_dur_sum AS
+SELECT launch_id, state, is_main_thread, thread_name, SUM(dur) AS dur
+FROM launch_threads_by_thread_state l
+WHERE
+ is_main_thread OR
+ -- Allowlist specific threads which need this. Do not add to this list
+ -- without careful consideration as every thread added here can cause
+ -- memory usage to balloon.
+ thread_name IN (
+ 'Jit thread pool'
+ )
+GROUP BY 1, 2, 3, 4;
+
+-- Given a launch id and thread state value, returns the aggregate sum
+-- of time spent in that state by the main thread of the process being started up.
+SELECT CREATE_FUNCTION(
+ 'MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launch_id INT, state STRING)',
+ 'INT',
+ '
+ SELECT SUM(dur)
+ FROM launch_thread_state_dur_sum l
+ WHERE l.launch_id = $launch_id AND state GLOB $state AND is_main_thread;
+ '
+);
+
+-- Given a launch id, thread state value and name of a thread, returns the aggregate sum
+-- of time spent in that state by that thread. Note: only threads of the processes
+-- being started are considered by this function - if a thread from a different name
+-- happens to match the name passed, it will *not* be included.
+SELECT CREATE_FUNCTION(
+ 'THREAD_TIME_FOR_LAUNCH_STATE_AND_THREAD(launch_id INT, state STRING, thread_name STRING)',
+ 'INT',
+ '
+ SELECT SUM(dur)
+ FROM launch_thread_state_dur_sum l
+ WHERE l.launch_id = $launch_id AND state GLOB $state AND thread_name = $thread_name;
+ '
+);
diff --git a/src/trace_processor/sqlite/create_function.cc b/src/trace_processor/sqlite/create_function.cc
index b77fcae..8aa5f92 100644
--- a/src/trace_processor/sqlite/create_function.cc
+++ b/src/trace_processor/sqlite/create_function.cc
@@ -90,9 +90,10 @@
int ret = sqlite3_step(ctx->stmt);
RETURN_IF_ERROR(
SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
- if (ret == SQLITE_DONE)
+ if (ret == SQLITE_DONE) {
// No return value means we just return don't set |out|.
return base::OkStatus();
+ }
PERFETTO_DCHECK(ret == SQLITE_ROW);
size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt));
@@ -104,6 +105,15 @@
}
out = sqlite_utils::SqliteValueToSqlValue(sqlite3_column_value(ctx->stmt, 0));
+
+ // If we return a bytes type but have a null pointer, SQLite will convert this
+ // to an SQL null. However, for proto build functions, we actively want to
+ // distinguish between nulls and 0 byte strings. Therefore, change the value
+ // to an empty string.
+ if (out.type == SqlValue::kBytes && out.bytes_value == nullptr) {
+ PERFETTO_DCHECK(out.bytes_count == 0);
+ out.bytes_value = "";
+ }
return base::OkStatus();
}
@@ -113,8 +123,9 @@
SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
if (ret == SQLITE_ROW) {
return base::ErrStatus(
- "%s: multiple values were returned when executing function body",
- ctx->prototype.function_name.c_str());
+ "%s: multiple values were returned when executing function body. "
+ "Executed SQL was %s",
+ ctx->prototype.function_name.c_str(), sqlite3_expanded_sql(ctx->stmt));
}
PERFETTO_DCHECK(ret == SQLITE_DONE);
diff --git a/src/trace_processor/sqlite/create_view_function.cc b/src/trace_processor/sqlite/create_view_function.cc
index f699951..01b6d31 100644
--- a/src/trace_processor/sqlite/create_view_function.cc
+++ b/src/trace_processor/sqlite/create_view_function.cc
@@ -257,7 +257,8 @@
});
auto col_to_arg_idx = [this](int col) {
- return static_cast<size_t>(col) - table_->return_values_.size();
+ return static_cast<uint32_t>(col) -
+ static_cast<uint32_t>(table_->return_values_.size());
};
size_t seen_hidden_constraints = 0;
@@ -317,7 +318,18 @@
// Bind all the arguments to the appropriate places in the function.
for (size_t i = 0; i < qc.constraints().size(); ++i) {
const auto& cs = qc.constraints()[i];
- const auto& arg = table_->prototype_.arguments[col_to_arg_idx(cs.column)];
+
+ // Don't deal with any constraints on the output parameters for simplicty.
+ // TODO(lalitm): reconsider this decision to allow more efficient queries:
+ // we would need to wrap the query in a SELECT * FROM (...) WHERE constraint
+ // like we do for SPAN JOIN.
+ if (!table_->schema().columns()[static_cast<size_t>(cs.column)].hidden())
+ continue;
+
+ uint32_t index = col_to_arg_idx(cs.column);
+ PERFETTO_DCHECK(index < table_->prototype_.arguments.size());
+
+ const auto& arg = table_->prototype_.arguments[index];
auto status = MaybeBindArgument(stmt_, table_->prototype_.function_name,
arg, argv[i]);
if (!status.ok()) {
diff --git a/src/trace_processor/sqlite/span_join_operator_table.cc b/src/trace_processor/sqlite/span_join_operator_table.cc
index 3e3a641..48104fe 100644
--- a/src/trace_processor/sqlite/span_join_operator_table.cc
+++ b/src/trace_processor/sqlite/span_join_operator_table.cc
@@ -69,13 +69,15 @@
case SQLITE_INDEX_CONSTRAINT_LT:
return "<";
case SQLITE_INDEX_CONSTRAINT_LIKE:
- return "like";
+ return " like ";
+ case SQLITE_INDEX_CONSTRAINT_GLOB:
+ return " glob ";
case SQLITE_INDEX_CONSTRAINT_ISNULL:
// The "null" will be added below in EscapedSqliteValueAsString.
return " is ";
case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
// The "null" will be added below in EscapedSqliteValueAsString.
- return " is not";
+ return " is not ";
default:
PERFETTO_FATAL("Operator to string conversion not impemented for %d", op);
}