blob: c26d06914eff15926961ba18b4e7bb4d3f51157a [file] [log] [blame]
-- Copyright 2023 The Chromium Authors
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
-- Checks if slice has an ancestor with provided name.
CREATE PERFETTO FUNCTION _has_parent_slice_with_name(
-- Id of the slice to check parents of.
id LONG,
-- Name of potential ancestor slice.
parent_name STRING
)
-- Whether `parent_name` is a name of an ancestor slice.
RETURNS BOOL AS
SELECT
EXISTS(
SELECT
1
FROM ancestor_slice($id)
WHERE
name = $parent_name
LIMIT 1
);
-- Returns the mojo ipc hash for a given task, looking it up from the
-- argument of descendant ScopedSetIpcHash slice.
-- This is relevant only for the older Chrome traces, where mojo IPC
-- hash was reported in a separate ScopedSetIpcHash slice.
CREATE PERFETTO FUNCTION _extract_mojo_ipc_hash(
slice_id LONG
)
RETURNS LONG AS
SELECT
extract_arg(arg_set_id, "chrome_mojo_event_info.ipc_hash")
FROM descendant_slice($slice_id)
WHERE
name = "ScopedSetIpcHash"
ORDER BY
id
LIMIT 1;
-- Returns the frame type (main frame vs subframe) for key navigation tasks
-- which capture the associated RenderFrameHost in an argument.
CREATE PERFETTO FUNCTION _extract_frame_type(
slice_id LONG
)
RETURNS LONG AS
SELECT
extract_arg(arg_set_id, "render_frame_host.frame_type")
FROM descendant_slice($slice_id)
WHERE
name IN ("RenderFrameHostImpl::BeginNavigation", "RenderFrameHostImpl::DidCommitProvisionalLoad", "RenderFrameHostImpl::DidCommitSameDocumentNavigation", "RenderFrameHostImpl::DidStopLoading")
LIMIT 1;
-- Human-readable aliases for a few key navigation tasks.
CREATE PERFETTO FUNCTION _human_readable_navigation_task_name(
task_name STRING
)
RETURNS STRING AS
SELECT
CASE
WHEN $task_name = "content.mojom.FrameHost message (hash=2168461044)"
THEN "FrameHost::BeginNavigation"
WHEN $task_name = "content.mojom.FrameHost message (hash=3561497419)"
THEN "FrameHost::DidCommitProvisionalLoad"
WHEN $task_name = "content.mojom.FrameHost message (hash=1421450774)"
THEN "FrameHost::DidCommitSameDocumentNavigation"
WHEN $task_name = "content.mojom.FrameHost message (hash=368650583)"
THEN "FrameHost::DidStopLoading"
END;
-- Takes a task name and formats it correctly for scheduler tasks.
CREATE PERFETTO FUNCTION _format_scheduler_task_name(
task_name STRING
)
RETURNS STRING AS
SELECT
printf("RunTask(posted_from=%s)", $task_name);
-- Takes the category and determines whether it is "Java" only, as opposed to
-- "toplevel,Java".
CREATE PERFETTO FUNCTION _java_not_top_level_category(
category STRING
)
RETURNS BOOL AS
SELECT
$category GLOB "*Java*" AND NOT $category GLOB "*toplevel*";
-- Takes the category and determines whether is any valid
-- toplevel category or combination of categories.
CREATE PERFETTO FUNCTION _any_top_level_category(
category STRING
)
RETURNS BOOL AS
SELECT
$category IN ("toplevel", "toplevel,viz", "toplevel,Java");
-- TODO(altimin): the situations with kinds in this file is a bit of a mess.
-- The idea is that it should work as `type` in the `slice` table, pointing to
-- a "child" table with more information about the task (e.g. posted_from for
-- scheduler tasks). Currently this is not the case and needs a cleanup.
-- Also we should align this with how table inheritance should work for
-- `CREATE PERFETTO TABLE`.
-- Get task type for a given task kind.
CREATE PERFETTO FUNCTION _get_java_views_task_type(
kind STRING
)
RETURNS STRING AS
SELECT
CASE $kind
WHEN "Choreographer"
THEN "choreographer"
WHEN "SingleThreadProxy::BeginMainFrame"
THEN "ui_thread_begin_main_frame"
END;
-- All slices corresponding to receiving mojo messages.
-- On the newer Chrome versions, it's just "Receive mojo message" and
-- "Receive mojo reply" slices (or "Receive {mojo_message_name}" if
-- built with `extended_tracing_enabled`. On legacy Chrome versions,
-- other appropriate messages (like "Connector::DispatchMessage") are used.
--
-- @column STRING interface_name Name of the IPC interface.
-- @column INT ipc_hash Hash of a message name.
-- @column STRING message_type Either 'message' or 'reply'.
-- @column INT id Slice id.
--
-- Note: this might include messages received within a sync mojo call.
-- TODO(altimin): This should use EXTEND_TABLE when it becomes available.
CREATE PERFETTO TABLE _chrome_mojo_slices AS
WITH
-- Select all new-style (post crrev.com/c/3270337) mojo slices and
-- generate |task_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(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,
id
FROM slice
WHERE
category GLOB '*toplevel*' AND name GLOB 'Receive *'
),
-- Select old-style slices for channel-associated mojo events.
old_associated_mojo_slices AS (
SELECT
name AS interface_name,
_extract_mojo_ipc_hash(id) AS ipc_hash,
"message" AS message_type,
id
FROM slice
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(arg_set_id, "chrome_mojo_event_info.watcher_notify_interface_tag"),
extract_arg(arg_set_id, "chrome_mojo_event_info.mojo_interface_tag")
) AS interface_name,
_extract_mojo_ipc_hash(id) AS ipc_hash,
"message" AS message_type,
id
FROM slice
WHERE
category GLOB "*toplevel*" AND name = "Connector::DispatchMessage"
),
merged AS (
-- 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
)
SELECT
*
FROM merged
ORDER BY
id;
-- This table contains a list of slices corresponding to the _representative_
-- Chrome Java view operations.
-- These are the outermost Java view slices after filtering out generic framework views
-- (like FitWindowsLinearLayout) and selecting the outermost slices from the remaining ones.
--
-- @column id INT Slice id.
-- @column ts INT Timestamp.
-- @column dur INT Duration.
-- @column name STRING Name of the view.
-- @column is_software_screenshot BOOL Whether this slice is a part of non-accelerated
-- capture toolbar screenshot.
-- @column is_hardware_screenshot BOOL Whether this slice is a part of accelerated
-- capture toolbar screenshot.
CREATE PERFETTO TABLE _chrome_java_views 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 AS s1
-- Ensure that toplevel Java slices are not included, as they may be logged
-- with either category = "toplevel" or category = "toplevel,Java".
-- Also filter out the zero duration slices as an attempt to reduce noise as
-- "Java" category contains misc events (as it's hard to add new categories).
WHERE
_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
-- The names below correspond respectively to:
-- * AndroidX.
-- * Other non-Chrome UI libraries.
-- * Generic Chrome frameworks.
-- * Non-specific Chrome slices.
-- * Screenshots custom annotations.
-- * Non-bytecode generated slices.
WHERE
NOT name IN ("FitWindowsFrameLayout", "FitWindowsLinearLayout", "ContentFrameLayout", "CoordinatorLayout")
AND NOT name IN ("ComponentHost")
AND NOT name IN ("CompositorView:finalizeLayers", "CompositorViewHolder", "CompositorViewHolder:layout", "CompositorViewHolder:updateContentViewChildrenDimension", "CoordinatorLayoutForPointer", "OptimizedFrameLayout", "ViewResourceAdapter:getBitmap", "ViewResourceFrameLayout")
AND NOT name IN ("AppCompatImageButton", "ScrollingBottomViewResourceFrameLayout")
AND NOT name IN ("ViewResourceAdapter:captureWithHardwareDraw", "ViewResourceAdapter:captureWithSoftwareDraw")
AND NOT name IN ("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 AS s1
-- We select "outermost" interesting slices: interesting slices which
-- do not another interesting slice in their parent chain.
WHERE
(
SELECT
count()
FROM ancestor_slice(s1.id) AS s2
JOIN interesting_java_slices AS s3
ON s2.id = s3.id
) = 0;
-- A list of slices corresponding to operations on interesting (non-generic)
-- Chrome Java views. The view is considered interested if it's not a system
-- (ContentFrameLayout) or generic library (CompositorViewHolder) views.
--
-- TODO(altimin): Add "columns_from slice" annotation.
-- TODO(altimin): convert this to EXTEND_TABLE when it becomes available.
CREATE PERFETTO VIEW chrome_java_views (
-- Name of the view.
filtered_name STRING,
-- Whether this slice is a part of non-accelerated capture toolbar screenshot.
is_software_screenshot BOOL,
-- Whether this slice is a part of accelerated capture toolbar screenshot.
is_hardware_screenshot BOOL,
-- Slice id.
slice_id LONG
) AS
SELECT
java_view.name AS filtered_name,
java_view.is_software_screenshot,
java_view.is_hardware_screenshot,
slice.id AS slice_id
FROM _chrome_java_views AS java_view
JOIN slice
USING (id);
-- A list of Choreographer tasks (Android frame generation) in Chrome.
CREATE PERFETTO VIEW _chrome_choreographer_tasks AS
SELECT
id,
"Choreographer" AS kind,
ts,
dur,
name
FROM slice
WHERE
name GLOB "Looper.dispatch: android.view.Choreographer$FrameHandler*";
-- Extract task's posted_from information from task's arguments.
CREATE PERFETTO FUNCTION _get_posted_from(
arg_set_id LONG
)
RETURNS STRING AS
WITH
posted_from AS (
SELECT
extract_arg($arg_set_id, "task.posted_from.file_name") AS file_name,
extract_arg($arg_set_id, "task.posted_from.function_name") AS function_name
)
SELECT
file_name || ":" || function_name AS posted_from
FROM posted_from;
-- Selects the BeginMainFrame slices (which as posted from ScheduledActionSendBeginMainFrame),
-- used for root-level processing. In top-level/Java based slices, these will correspond to 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.
CREATE PERFETTO FUNCTION _select_begin_main_frame_java_slices(
name STRING
)
RETURNS TABLE (
id LONG,
kind STRING,
ts TIMESTAMP,
dur DURATION,
name STRING
) AS
SELECT
id,
"SingleThreadProxy::BeginMainFrame" AS kind,
ts,
dur,
name
FROM slice
WHERE
(
name = $name
AND _get_posted_from(arg_set_id) = "cc/trees/single_thread_proxy.cc:ScheduledActionSendBeginMainFrame"
);
-- A list of Chrome tasks which were performing operations with Java views,
-- together with the names of these views.
-- @column id INT Slice id.
-- @column kind STRING Type of the task.
-- @column java_views STRING Concatenated names of Java views used by the task.
CREATE PERFETTO VIEW _chrome_slices_with_java_views AS
WITH
-- Select UI thread BeginMainFrames (which are Chrome scheduler tasks) and
-- Choreographer frames (which are looper tasks).
root_slices AS (
SELECT
id,
kind
FROM _select_begin_main_frame_java_slices('ThreadControllerImpl::RunTask')
UNION ALL
SELECT
id,
kind
FROM _chrome_choreographer_tasks
),
-- Intermediate step to allow us to sort java view names.
root_slice_and_java_view_not_grouped AS (
SELECT
root.id,
root.kind,
java_view.name AS java_view_name
FROM root_slices AS root, descendant_slice(root.id) AS child
JOIN _chrome_java_views AS java_view
ON java_view.id = child.id
)
SELECT
root.id,
root.kind,
GROUP_CONCAT(DISTINCT java_view.java_view_name) AS java_views
FROM root_slices AS root
LEFT JOIN root_slice_and_java_view_not_grouped AS java_view
USING (id)
GROUP BY
root.id;
-- A list of tasks executed by Chrome scheduler.
CREATE PERFETTO TABLE _chrome_scheduler_tasks AS
SELECT
id
FROM slice
WHERE
category GLOB "*toplevel*"
AND (
name = "ThreadControllerImpl::RunTask" OR name = "ThreadPool_RunTask"
)
ORDER BY
id;
-- A list of tasks executed by Chrome scheduler.
CREATE PERFETTO VIEW chrome_scheduler_tasks (
-- Slice id.
id LONG,
-- Type.
type STRING,
-- Name of the task.
name STRING,
-- Timestamp.
ts TIMESTAMP,
-- Duration.
dur DURATION,
-- Utid of the thread this task run on.
utid LONG,
-- Name of the thread this task run on.
thread_name STRING,
-- Upid of the process of this task.
upid LONG,
-- Name of the process of this task.
process_name STRING,
-- Same as slice.track_id.
track_id LONG,
-- Same as slice.category.
category STRING,
-- Same as slice.depth.
depth LONG,
-- Same as slice.parent_id.
parent_id LONG,
-- Same as slice.arg_set_id.
arg_set_id LONG,
-- Same as slice.thread_ts.
thread_ts TIMESTAMP,
-- Same as slice.thread_dur.
thread_dur DURATION,
-- Source location where the PostTask was called.
posted_from STRING
) AS
SELECT
task.id,
"chrome_scheduler_tasks" AS type,
_format_scheduler_task_name(_get_posted_from(slice.arg_set_id)) AS name,
slice.ts,
slice.dur,
thread.utid,
thread.name AS thread_name,
process.upid,
process.name AS process_name,
slice.track_id,
slice.category,
slice.depth,
slice.parent_id,
slice.arg_set_id,
slice.thread_ts,
slice.thread_dur,
_get_posted_from(slice.arg_set_id) AS posted_from
FROM _chrome_scheduler_tasks AS task
JOIN slice
USING (id)
JOIN thread_track
ON slice.track_id = thread_track.id
JOIN thread
USING (utid)
JOIN process
USING (upid)
ORDER BY
task.id;
-- Select the slice that might be the descendant mojo slice for the given task
-- slice if it exists.
CREATE PERFETTO FUNCTION _get_descendant_mojo_slice_candidate(
slice_id LONG
)
RETURNS LONG AS
SELECT
id
FROM descendant_slice($slice_id)
WHERE
-- The tricky case here is dealing with sync mojo IPCs: we do not want to
-- pick up sync IPCs when we are in a non-IPC task.
-- So we look at all toplevel events and pick up the first one:
-- for sync mojo messages, it will be "Send mojo message", which then
-- will fail.
-- Some events are excluded as they can legimately appear under "RunTask"
-- before "Receive mojo message".
category GLOB "*toplevel*"
AND NOT name IN ("SimpleWatcher::OnHandleReady", "MessagePipe peer closed")
ORDER BY
depth,
ts
LIMIT 1;
CREATE PERFETTO FUNCTION _descendant_mojo_slice(
slice_id LONG
)
RETURNS TABLE (
task_name STRING
) AS
SELECT
printf("%s %s (hash=%d)", mojo.interface_name, mojo.message_type, mojo.ipc_hash) AS task_name
FROM slice AS task
JOIN _chrome_mojo_slices AS mojo
ON mojo.id = _get_descendant_mojo_slice_candidate($slice_id)
WHERE
task.id = $slice_id;
-- A list of "Chrome tasks": top-level execution units (e.g. scheduler tasks /
-- IPCs / system callbacks) run by Chrome. For a given thread, the tasks
-- will not intersect.
--
-- @column task_name STRING Name for the given task.
-- @column task_type STRING Type of the task (e.g. "scheduler").
-- @column scheduling_delay LONG
CREATE PERFETTO TABLE _chrome_tasks 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
WHERE
_any_top_level_category(category)
AND (
SELECT
count()
FROM ancestor_slice(slice.id) AS anc
WHERE
anc.category GLOB "*toplevel*" OR anc.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
id,
name AS task_name,
"java" AS task_type
FROM slice AS s
WHERE
_java_not_top_level_category(category)
AND (
SELECT
count()
FROM ancestor_slice(s.id) AS s2
WHERE
s2.category GLOB "*toplevel*" OR s2.category GLOB "*Java*"
) = 0
),
-- Generate full names for tasks with java views.
java_views_tasks AS (
SELECT
id,
printf('%s(java_views=%s)', kind, java_views) AS task_name,
_get_java_views_task_type(kind) AS task_type
FROM _chrome_slices_with_java_views
),
scheduler_tasks AS (
SELECT
id,
name AS task_name,
"scheduler" AS task_type
FROM chrome_scheduler_tasks
),
-- 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
-- We use the "RunTask" as the task, and pick up the name from its child
-- "Receive mojo message" event.
task.id,
receive_message.task_name,
"mojo" AS task_type
FROM chrome_scheduler_tasks AS task, _descendant_mojo_slice(task.id) AS receive_message
WHERE
task.posted_from IN ("mojo/public/cpp/system/simple_watcher.cc:Notify", "mojo/public/cpp/system/simple_watcher.cc:ArmOrNotify", "mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe", "ipc/ipc_mojo_bootstrap.cc:Accept")
),
navigation_tasks AS (
WITH
tasks_with_readable_names AS (
SELECT
id,
_human_readable_navigation_task_name(task_name) AS readable_name,
coalesce(_extract_frame_type(id), 'unknown frame type') AS frame_type
FROM scheduler_tasks_with_mojo
)
SELECT
id,
printf("%s (%s)", readable_name, frame_type) AS task_name,
'navigation_task' AS task_type
FROM tasks_with_readable_names
WHERE
readable_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_task_name AS (
SELECT
task.id AS id,
coalesce(
navigation.task_name,
java_views.task_name,
mojo.task_name,
scheduler.task_name,
task.name
) AS name,
coalesce(navigation.task_type, java_views.task_type, mojo.task_type, scheduler.task_type, "other") AS task_type
FROM non_embedded_toplevel_slices AS task
LEFT JOIN scheduler_tasks_with_mojo AS mojo
ON mojo.id = task.id
LEFT JOIN scheduler_tasks AS scheduler
ON scheduler.id = task.id
LEFT JOIN java_views_tasks AS java_views
ON java_views.id = task.id
LEFT JOIN navigation_tasks AS navigation
ON navigation.id = task.id
)
-- Merge slices from toplevel and Java categories.
SELECT
*
FROM non_embedded_toplevel_slices_with_task_name
UNION ALL
SELECT
*
FROM non_embedded_java_slices
ORDER BY
id;
-- A list of "Chrome tasks": top-level execution units (e.g. scheduler tasks /
-- IPCs / system callbacks) run by Chrome. For a given thread, the slices
-- corresponding to these tasks will not intersect.
CREATE PERFETTO VIEW chrome_tasks (
-- Id for the given task, also the id of the slice this task corresponds to.
id LONG,
-- Name for the given task.
name STRING,
-- Type of the task (e.g. "scheduler").
task_type STRING,
-- Thread name.
thread_name STRING,
-- Utid.
utid LONG,
-- Process name.
process_name STRING,
-- Upid.
upid LONG,
-- Alias of |slice.ts|.
ts TIMESTAMP,
-- Alias of |slice.dur|.
dur DURATION,
-- Alias of |slice.track_id|.
track_id LONG,
-- Alias of |slice.category|.
category STRING,
-- Alias of |slice.arg_set_id|.
arg_set_id LONG,
-- Alias of |slice.thread_ts|.
thread_ts TIMESTAMP,
-- Alias of |slice.thread_dur|.
thread_dur DURATION,
-- STRING Legacy alias for |name|.
full_name STRING
) AS
SELECT
cti.id,
cti.name,
task_type,
thread.name AS thread_name,
thread.utid,
process.name AS process_name,
thread.upid,
s.ts,
s.dur,
s.track_id,
s.category,
s.arg_set_id,
s.thread_ts,
s.thread_dur,
cti.name AS full_name
FROM _chrome_tasks AS cti
JOIN slice AS s
ON cti.id = s.id
JOIN thread_track AS tt
ON s.track_id = tt.id
JOIN thread
USING (utid)
JOIN process
USING (upid);