-- 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 INT, -- 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 INT) RETURNS INT 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 INT) RETURNS INT 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 $category not 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 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" ) -- 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| table, add an index on -- id to make lookups fast. CREATE INDEX _chrome_mojo_slices_idx ON _chrome_mojo_slices(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 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 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 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 -- 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) s2 JOIN interesting_java_slices 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 INT ) 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 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 INT) 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 INT, kind STRING, ts LONG, dur LONG, 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 root JOIN descendant_slice(root.id) child JOIN _chrome_java_views 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 root LEFT JOIN root_slice_and_java_view_not_grouped java_view USING (id) GROUP BY root.id; -- A list of tasks executed by Chrome scheduler. CREATE 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 INT, -- Type. type STRING, -- Name of the task. name STRING, -- Timestamp. ts INT, -- Duration. dur INT, -- Utid of the thread this task run on. utid INT, -- Name of the thread this task run on. thread_name STRING, -- Upid of the process of this task. upid INT, -- Name of the process of this task. process_name STRING, -- Same as slice.track_id. track_id INT, -- Same as slice.category. category STRING, -- Same as slice.depth. depth INT, -- Same as slice.parent_id. parent_id INT, -- Same as slice.arg_set_id. arg_set_id INT, -- Same as slice.thread_ts. thread_ts INT, -- Same as slice.thread_dur. thread_dur INT, -- 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 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 INT ) RETURNS INT 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 name NOT IN ( "SimpleWatcher::OnHandleReady", "MessagePipe peer closed") ORDER by depth, ts LIMIT 1; CREATE PERFETTO FUNCTION _descendant_mojo_slice(slice_id INT) 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 task JOIN _chrome_mojo_slices 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 INT CREATE 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) 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 s WHERE _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 ), -- 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 task JOIN _DESCENDANT_MOJO_SLICE(task.id) 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, IFNULL(_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 task LEFT JOIN scheduler_tasks_with_mojo mojo ON mojo.id = task.id LEFT JOIN scheduler_tasks scheduler ON scheduler.id = task.id LEFT JOIN java_views_tasks java_views ON java_views.id = task.id LEFT JOIN navigation_tasks 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 INT, -- Name for the given task. name STRING, -- Type of the task (e.g. "scheduler"). task_type STRING, -- Thread name. thread_name STRING, -- Utid. utid INT, -- Process name. process_name STRING, -- Upid. upid INT, -- Alias of |slice.ts|. ts INT, -- Alias of |slice.dur|. dur INT, -- Alias of |slice.track_id|. track_id INT, -- Alias of |slice.category|. category INT, -- Alias of |slice.arg_set_id|. arg_set_id INT, -- Alias of |slice.thread_ts|. thread_ts INT, -- Alias of |slice.thread_dur|. thread_dur INT, -- 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 cti JOIN slice s ON cti.id = s.id JOIN thread_track tt ON s.track_id = tt.id JOIN thread USING (utid) JOIN process USING (upid);