blob: d2886f1f5941ff5cc8284884382de31afc70c5a2 [file] [log] [blame]
--
-- 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.
--
SELECT IMPORT("common.slices");
SELECT CREATE_FUNCTION(
'{{function_prefix}}EXTRACT_MOJO_IPC_HASH(slice_id INT)',
'INT',
'
SELECT EXTRACT_ARG(s2.arg_set_id, "chrome_mojo_event_info.ipc_hash")
FROM descendant_slice($slice_id) s2
WHERE s2.name="ScopedSetIpcHash"
ORDER BY s2.id
LIMIT 1
'
);
SELECT CREATE_FUNCTION(
'{{function_prefix}}EXTRACT_FRAME_TYPE(slice_id INT)',
'INT',
'
SELECT EXTRACT_ARG(descendants.arg_set_id, "render_frame_host.frame_type")
FROM descendant_slice($slice_id) descendants
WHERE descendants.name IN ("RenderFrameHostImpl::BeginNavigation",
"RenderFrameHostImpl::DidCommitProvisionalLoad",
"RenderFrameHostImpl::DidCommitSameDocumentNavigation",
"RenderFrameHostImpl::DidStopLoading")
LIMIT 1
'
);
-- Selects the ScheduledActionSendBeginMainFrame slices, used for root-level
-- processing. In top-level/Java based slices, these will correspond with the
-- ancestor of descendant slices; in long-task tracking, these tasks will be
-- on a custom track and will need to be associated with children by timestamp
-- and duration. Corresponds with the Choreographer root slices in
-- chrome_choreographer_tasks below.
--
-- Schema:
-- @column is The slice id.
-- @column kind The type of Java slice.
-- @column ts The timestamp of the slice.
-- @column name The name of the slice.
--
-- Power states are encoded as non-negative integers, with zero representing
-- full-power operation and positive values representing increasingly deep
-- sleep states.
SELECT CREATE_VIEW_FUNCTION(
'SELECT_BEGIN_MAIN_FRAME_JAVA_SLICES(name STRING)',
'id INT, kind STRING, ts LONG, dur LONG, name STRING',
'SELECT
id,
"SingleThreadProxy::BeginMainFrame" AS kind,
ts,
dur,
name
FROM slice
WHERE
(name = $name
AND EXTRACT_ARG(arg_set_id, "task.posted_from.file_name") = "cc/trees/single_thread_proxy.cc"
AND EXTRACT_ARG(arg_set_id, "task.posted_from.function_name") = "ScheduledActionSendBeginMainFrame")
'
);
SELECT CREATE_FUNCTION(
-- Function prototype: takes a formatted chrome scheduler task name and
-- returns a readable task name.
'{{function_prefix}}HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name STRING)',
'STRING',
'SELECT
CASE
WHEN $full_name = "content.mojom.FrameHost message (hash=2168461044)" THEN "FrameHost::BeginNavigation"
WHEN $full_name = "content.mojom.FrameHost message (hash=3561497419)" THEN "FrameHost::DidCommitProvisionalLoad"
WHEN $full_name = "content.mojom.FrameHost message (hash=1421450774)" THEN "FrameHost::DidCommitSameDocumentNavigation"
WHEN $full_name = "content.mojom.FrameHost message (hash=368650583)" THEN "FrameHost::DidStopLoading"
END
'
);
SELECT CREATE_FUNCTION(
-- Function prototype: takes a task name and formats it correctly for
-- scheduler tasks.
'{{function_prefix}}FORMAT_SCHEDULER_TASK_NAME(full_name STRING)',
'STRING',
'SELECT
printf("RunTask(posted_from=%s)", $full_name)
'
);
SELECT CREATE_FUNCTION(
-- Function prototype: takes the category and determines whether it is "Java"
-- only, as opposed to "toplevel,Java".
'{{function_prefix}}JAVA_NOT_TOP_LEVEL_CATEGORY(category STRING)',
'BOOL',
'SELECT
$category GLOB "*Java*" AND $category not GLOB "*toplevel*"
'
);
SELECT CREATE_FUNCTION(
-- Function prototype: takes the category and determines whether is any valid
-- toplevel category or combination of categories.
'{{function_prefix}}ANY_TOP_LEVEL_CATEGORY(category STRING)',
'BOOL',
'SELECT
$category IN ("toplevel", "toplevel,viz", "toplevel,Java")
'
);
SELECT CREATE_FUNCTION(
-- Function prototype: takes a task name and formats it correctly for
-- scheduler tasks.
'{{function_prefix}}GET_JAVA_VIEWS_TASK_TYPE(kind STRING)',
'STRING',
'SELECT
(CASE $kind
WHEN "Choreographer" THEN "choreographer"
WHEN "SingleThreadProxy::BeginMainFrame" THEN "ui_thread_begin_main_frame"
END)
'
);
-- Create |chrome_mojo_slices_tbl| table, containing a subset of slice
-- table with the slices corresponding to mojo messages.
--
-- Note: this might include messages received within a sync mojo call.
DROP TABLE IF EXISTS chrome_mojo_slices_tbl;
CREATE TABLE chrome_mojo_slices_tbl AS
WITH
-- Select all new-style (post crrev.com/c/3270337) mojo slices and
-- generate |full_name| for them.
-- If extended tracing is enabled, the slice name will have the full method
-- name (i.e. "Receive content::mojom::FrameHost::DidStopLoading") and we
-- should use it as a full name.
-- If extended tracing is not enabled, we should include the interface name
-- and method hash into the full name.
new_mojo_slices AS (
SELECT
EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag") AS interface_name,
EXTRACT_ARG(arg_set_id, "chrome_mojo_event_info.ipc_hash") AS ipc_hash,
CASE name
WHEN "Receive mojo message" THEN "message"
WHEN "Receive mojo reply" THEN "reply"
END AS message_type,
s.id
FROM {{slice_table_name}} s
WHERE
category GLOB "*toplevel*"
AND name GLOB 'Receive *'
),
-- Select old-style slices for channel-associated mojo events.
old_associated_mojo_slices AS (
SELECT
s.name AS interface_name,
{{function_prefix}}EXTRACT_MOJO_IPC_HASH(s.id) AS ipc_hash,
"message" AS message_type,
s.id
FROM {{slice_table_name}} s
WHERE
category GLOB "*mojom*"
AND name GLOB '*.mojom.*'
),
-- Select old-style slices for non-(channel-associated) mojo events.
old_non_associated_mojo_slices AS (
SELECT
COALESCE(
EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.watcher_notify_interface_tag"),
EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag")
) AS interface_name,
{{function_prefix}}EXTRACT_MOJO_IPC_HASH(s.id) AS ipc_hash,
"message" AS message_type,
s.id
FROM {{slice_table_name}} s
WHERE
category GLOB "*toplevel*" AND name = "Connector::DispatchMessage"
)
-- Merge all mojo slices.
SELECT * FROM new_mojo_slices
UNION ALL
SELECT * FROM old_associated_mojo_slices
UNION ALL
SELECT * FROM old_non_associated_mojo_slices;
-- As we lookup by ID on |chrome_mojo_slices_tbl| table, add an index on
-- id to make lookups fast.
DROP INDEX IF EXISTS chrome_mojo_slices_idx;
CREATE INDEX chrome_mojo_slices_idx ON chrome_mojo_slices_tbl(id);
-- This table contains a list of slices corresponding to the _representative_ Chrome Java views.
-- These are the outermost Java view slices after filtering out generic framework views
-- (like FitWindowsLinearLayout) and selecting the outermost slices from the remaining ones.
DROP TABLE IF EXISTS chrome_java_views_internal;
CREATE TABLE chrome_java_views_internal AS
WITH
-- .draw, .onLayout and .onMeasure parts of the java view names don't add much, strip them.
java_slices_with_trimmed_names AS (
SELECT
id,
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
s1.name,
".draw", ""),
".onLayout", ""),
".onMeasure", ""),
".Layout", ""),
".Measure", "") AS name,
ts,
dur
FROM
{{slice_table_name}} s1
-- Ensure that toplevel Java slices are not included, as they may be logged
-- with either category = "toplevel" or category = "toplevel,Java".
WHERE {{function_prefix}}JAVA_NOT_TOP_LEVEL_CATEGORY(category) AND dur > 0
),
-- We filter out generic slices from various UI frameworks which don't tell us much about
-- what exactly this view is doing.
interesting_java_slices AS (
SELECT
id, name, ts, dur
FROM java_slices_with_trimmed_names
WHERE NOT name IN (
-- AndroidX.
"FitWindowsFrameLayout",
"FitWindowsLinearLayout",
"ContentFrameLayout",
"CoordinatorLayout",
-- Other non-Chrome UI libraries.
"ComponentHost",
-- Generic Chrome frameworks.
"CompositorView:finalizeLayers",
"CompositorViewHolder",
"CompositorViewHolder:layout",
"CompositorViewHolder:updateContentViewChildrenDimension",
"CoordinatorLayoutForPointer",
"OptimizedFrameLayout",
"ViewResourceAdapter:getBitmap",
"ViewResourceFrameLayout",
-- Non-specific Chrome slices.
"AppCompatImageButton",
"ScrollingBottomViewResourceFrameLayout",
-- Screenshots get their custom annotations below.
"ViewResourceAdapter:captureWithHardwareDraw",
"ViewResourceAdapter:captureWithSoftwareDraw",
-- Non-bytecode generated slices.
"LayoutDriver:onUpdate"
)
)
SELECT
s1.*,
-- While the parent slices are too generic to be used by themselves,
-- they can provide some useful metadata.
HAS_PARENT_SLICE_WITH_NAME(
s1.id,
"ViewResourceAdapter:captureWithSoftwareDraw"
) AS is_software_screenshot,
HAS_PARENT_SLICE_WITH_NAME(
s1.id,
"ViewResourceAdapter:captureWithHardwareDraw"
) AS is_hardware_screenshot
FROM interesting_java_slices s1
WHERE (SELECT count()
FROM ancestor_slice(s1.id) s2
JOIN interesting_java_slices s3 ON s2.id = s3.id) = 0;
-- |chrome_java_views| is a view over |chrome_java_views_internal| table, adding the necessary columns
-- from |slice|.
DROP VIEW IF EXISTS chrome_java_views;
CREATE VIEW chrome_java_views AS
SELECT
s1.name AS filtered_name,
s1.is_software_screenshot,
s1.is_hardware_screenshot,
s2.*
FROM chrome_java_views_internal s1
JOIN {{slice_table_name}} s2 USING (id);
DROP VIEW IF EXISTS chrome_choreographer_tasks;
CREATE VIEW chrome_choreographer_tasks
AS
SELECT
id,
"Choreographer" AS kind,
ts,
dur,
name
FROM slice
WHERE
name GLOB "Looper.dispatch: android.view.Choreographer$FrameHandler*";
-- Most of java views will be triggered either by Chrome's BeginMainFrame
-- or by Android's Choreographer.
DROP VIEW IF EXISTS chrome_slices_with_java_views_internal;
CREATE VIEW chrome_slices_with_java_views_internal AS
WITH
-- Select UI thread BeginMainFrames and Choreographer frames.
root_slices AS (
SELECT *
FROM SELECT_BEGIN_MAIN_FRAME_JAVA_SLICES('ThreadControllerImpl::RunTask')
UNION ALL
SELECT * FROM chrome_choreographer_tasks
),
-- Intermediate step to allow us to sort java view names.
root_slice_and_java_view_not_grouped AS (
SELECT
s1.id, s1.kind, s3.name AS java_view_name
FROM root_slices s1
JOIN descendant_slice(s1.id) s2
JOIN chrome_java_views_internal s3 ON s2.id = s3.id
)
SELECT
s1.id,
s1.kind,
GROUP_CONCAT(DISTINCT s2.java_view_name) AS java_views
FROM root_slices s1
LEFT JOIN root_slice_and_java_view_not_grouped s2 USING (id)
GROUP BY s1.id;
-- Create |chrome_tasks| table, which contains a subset of slice
-- table of the slices which should be considered top-level Chrome tasks with the
-- additional scheduler_type |full_name| column, derived from subevents.
DROP TABLE IF EXISTS chrome_tasks_internal;
CREATE TABLE chrome_tasks_internal AS
WITH
-- Select slices from "toplevel" category which do not have another
-- "toplevel" slice as ancestor. The possible cases include sync mojo messages
-- and tasks in nested runloops. Toplevel events may also be logged as with
-- the Java category.
non_embedded_toplevel_slices AS (
SELECT * FROM {{slice_table_name}} s
WHERE
{{function_prefix}}ANY_TOP_LEVEL_CATEGORY(category)
AND (SELECT count() FROM ancestor_slice(s.id) s2
WHERE s2.category GLOB "*toplevel*" or s2.category GLOB "*toplevel.viz*") = 0
),
-- Select slices from "Java" category which do not have another "Java" or
-- "toplevel" slice as parent. In the longer term they should probably belong
-- to "toplevel" category as well, but for now this will have to do. Ensure
-- that "Java" slices do not include "toplevel" slices as those would be
-- handled elsewhere.
non_embedded_java_slices AS (
SELECT name AS full_name, "java" AS task_type, id
FROM {{slice_table_name}} s
WHERE
{{function_prefix}}JAVA_NOT_TOP_LEVEL_CATEGORY(category)
AND (SELECT count()
FROM ancestor_slice(s.id) s2
WHERE s2.category GLOB "*toplevel*" OR s2.category GLOB "*Java*") = 0
),
raw_scheduler_tasks AS (
SELECT
EXTRACT_ARG(s.arg_set_id, "task.posted_from.file_name") AS posted_from_file_name,
EXTRACT_ARG(s.arg_set_id, "task.posted_from.function_name") AS posted_from_function_name,
(CASE name
WHEN "ThreadControllerImpl::RunTask" THEN "SequenceManager"
WHEN "ThreadPool_RunTask" THEN "ThreadPool"
END) AS scheduler_type,
s.id
FROM {{slice_table_name}} s
WHERE
category GLOB "*toplevel*"
AND (name = "ThreadControllerImpl::RunTask" OR name = "ThreadPool_RunTask")
),
scheduler_tasks AS (
SELECT
s1.posted_from_file_name || ":" || s1.posted_from_function_name AS posted_from,
s1.posted_from_file_name,
s1.posted_from_function_name,
s1.scheduler_type,
s1.id
FROM raw_scheduler_tasks s1
),
-- Generate full names for scheduler tasks.
scheduler_tasks_with_full_names AS (
SELECT
{{function_prefix}}FORMAT_SCHEDULER_TASK_NAME(s.posted_from) AS full_name,
"scheduler" AS task_type,
s.id
FROM scheduler_tasks s
),
-- Generate full names for mojo slices.
mojo_slices AS (
SELECT
printf('%s %s (hash=%d)',
interface_name, message_type, ipc_hash) AS full_name,
"mojo" AS task_type,
id
FROM chrome_mojo_slices_tbl
),
-- Generate full names for tasks with java views.
java_views_tasks AS (
SELECT
printf('%s(java_views=%s)', kind, java_views) AS full_name,
{{function_prefix}}GET_JAVA_VIEWS_TASK_TYPE(kind) AS task_type,
id
FROM chrome_slices_with_java_views_internal
),
-- Select scheduler tasks which are used to run mojo messages and use the mojo names
-- as full names for these slices.
-- We restrict this to specific scheduler tasks which are expected to run mojo
-- tasks due to sync mojo events, which also emit similar events.
scheduler_tasks_with_mojo AS (
SELECT
(SELECT s3.full_name
FROM descendant_slice(s1.id) s2
JOIN mojo_slices s3 USING (id)
ORDER BY s2.depth LIMIT 1) AS full_name,
"mojo" AS task_type,
s1.id
FROM
scheduler_tasks s1
WHERE
s1.posted_from IN (
"mojo/public/cpp/system/simple_watcher.cc:Notify",
"mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe",
"ipc/ipc_mojo_bootstrap.cc:Accept")
),
navigation_tasks AS (
SELECT
printf("%s (%s)",
{{function_prefix}}HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name),
IFNULL({{function_prefix}}EXTRACT_FRAME_TYPE(id), 'unknown frame type')) AS full_name,
'navigation_task' AS task_type,
id
FROM (
SELECT * FROM scheduler_tasks_with_mojo
WHERE {{function_prefix}}HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name) IS NOT NULL
)
),
-- Add scheduler and mojo full names to non-embedded slices from
-- the "toplevel" category, with mojo ones taking precedence.
non_embedded_toplevel_slices_with_full_name AS (
SELECT
COALESCE(s5.full_name, s4.full_name, s2.full_name, s3.full_name, s1.name) AS full_name,
COALESCE(s5.task_type, s4.task_type, s2.task_type, s3.task_type, "other") AS task_type,
s1.id AS id
FROM non_embedded_toplevel_slices s1
LEFT JOIN scheduler_tasks_with_mojo s2 ON s2.id = s1.id
LEFT JOIN scheduler_tasks_with_full_names s3 ON s3.id = s1.id
LEFT JOIN java_views_tasks s4 ON s4.id = s1.id
LEFT JOIN navigation_tasks s5 ON s5.id = s1.id
)
-- Merge slices from toplevel and Java categories.
SELECT * FROM non_embedded_toplevel_slices_with_full_name
UNION ALL
SELECT * FROM non_embedded_java_slices;
DROP VIEW IF EXISTS chrome_tasks;
CREATE VIEW chrome_tasks AS
SELECT
full_name,
task_type,
thread.name AS thread_name,
thread.utid,
process.name AS process_name,
thread.upid,
ts.*
FROM chrome_tasks_internal cti
JOIN {{slice_table_name}} ts USING (id)
JOIN thread_track tt ON ts.track_id = tt.id
JOIN thread USING (utid)
JOIN process USING (upid);
-- A helper view into Chrome thread slices which don't have a parent task.
-- TODO(altimin): Use chrome_thread here once it's reliable.
DROP VIEW IF EXISTS chrome_slices_without_parent_task;
CREATE VIEW chrome_slices_without_parent_task AS
SELECT
s1.*
FROM {{slice_table_name}} s1
LEFT JOIN chrome_tasks s2 USING (id)
WHERE
(SELECT count()
FROM ancestor_slice(s1.id) s3
JOIN chrome_tasks s4 ON s3.id = s4.id) = 0
AND s2.id IS NULL;