1-- 2-- Copyright 2024 The Android Open Source Project 3-- 4-- Licensed under the Apache License, Version 2.0 (the "License"); 5-- you may not use this file except in compliance with the License. 6-- You may obtain a copy of the License at 7-- 8-- https://www.apache.org/licenses/LICENSE-2.0 9-- 10-- Unless required by applicable law or agreed to in writing, software 11-- distributed under the License is distributed on an "AS IS" BASIS, 12-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13-- See the License for the specific language governing permissions and 14-- limitations under the License. 15-- 16 17INCLUDE PERFETTO MODULE graphs.search; 18 19-- Computes critical paths, the dependency graph of a task. 20-- The critical path is a set of edges reachable from a root node with the sum of the edge 21-- weights just exceeding the root node capacity. This ensures that the tasks in the critical path 22-- completely 'covers' the root capacity. 23-- Typically, every node represents a point in time on some task where it transitioned from 24-- idle to active state. 25-- 26-- Example usage on traces with Linux sched information: 27-- ``` 28-- -- Compute the userspace critical path from every task sleep. 29-- SELECT * FROM 30-- critical_path_intervals!( 31-- _wakeup_userspace_edges, 32-- (SELECT id AS root_node_id, prev_id - id FROM _wakeup_graph WHERE prev_id IS NOT NULL)); 33-- ``` 34CREATE PERFETTO MACRO _critical_path( 35 -- A table/view/subquery corresponding to a directed graph on which the 36 -- reachability search should be performed. This table must have the columns 37 -- "source_node_id", "dest_node_id" and "edge_weight" corresponding to the two nodes on 38 -- either end of the edges in the graph and the edge weight. 39 -- 40 -- Note: the columns must contain uint32 similar to ids in trace processor 41 -- tables (i.e. the values should be relatively dense and close to zero). The 42 -- implementation makes assumptions on this for performance reasons and, if 43 -- this criteria is not, can lead to enormous amounts of memory being 44 -- allocated. 45 -- An edge weight is the absolute difference between the node ids forming the edge. 46 graph_table TableOrSubQuery, 47 -- A table/view/subquery corresponding to start nodes to |graph_table| which will be the 48 -- roots of the reachability trees. This table must have the columns 49 -- "root_node_id" and "capacity" corresponding to the starting node id and the capacity 50 -- of the root node to contain edge weights. 51 -- 52 -- Note: the columns must contain uint32 similar to ids in trace processor 53 -- tables (i.e. the values should be relatively dense and close to zero). The 54 -- implementation makes assumptions on this for performance reasons and, if 55 -- this criteria is not, can lead to enormous amounts of memory being 56 -- allocated. 57 root_table TableOrSubQuery 58) 59-- The returned table has the schema (root_id LONG, id LONG, parent_id LONG). 60-- |root_id| is the id of the root where the critical path computation started. 61-- |id| is the id of a node in the critical path and |parent_id| is the predecessor of |id|. 62RETURNS TableOrSubQuery AS 63( 64 WITH 65 _edges AS ( 66 SELECT 67 source_node_id, 68 dest_node_id, 69 edge_weight 70 FROM $graph_table 71 ), 72 _roots AS ( 73 SELECT 74 root_node_id, 75 capacity AS root_target_weight 76 FROM $root_table 77 ), 78 _search_bounds AS ( 79 SELECT 80 min(root_node_id - root_target_weight) AS min_wakeup, 81 max(root_node_id + root_target_weight) AS max_wakeup 82 FROM _roots 83 ), 84 _graph AS ( 85 SELECT 86 source_node_id, 87 coalesce(dest_node_id, source_node_id) AS dest_node_id, 88 edge_weight 89 FROM _edges, _search_bounds 90 WHERE 91 source_node_id BETWEEN min_wakeup AND max_wakeup AND source_node_id IS NOT NULL 92 ) 93 SELECT DISTINCT 94 root_node_id AS root_id, 95 parent_node_id AS parent_id, 96 node_id AS id 97 FROM graph_reachable_weight_bounded_dfs !(_graph, _roots, 1) AS cr 98); 99 100-- Flattens overlapping tasks within a critical path and flattens overlapping critical paths. 101CREATE PERFETTO MACRO _critical_path_to_intervals( 102 critical_path_table TableOrSubquery, 103 node_table TableOrSubquery 104) 105RETURNS TableOrSubquery AS 106( 107 WITH 108 flat_tasks AS ( 109 SELECT 110 node.ts, 111 cr.root_id, 112 cr.id, 113 lead(node.ts) OVER (PARTITION BY cr.root_id ORDER BY cr.id) - node.ts AS dur 114 FROM $critical_path_table AS cr 115 JOIN $node_table AS node 116 USING (id) 117 ), 118 span_starts AS ( 119 SELECT 120 max(cr.ts, idle.ts - idle_dur) AS ts, 121 idle.ts AS idle_end_ts, 122 cr.ts + cr.dur AS cr_end_ts, 123 cr.id, 124 cr.root_id 125 FROM flat_tasks AS cr 126 JOIN $node_table AS idle 127 ON cr.root_id = idle.id 128 ) 129 SELECT 130 ts, 131 min(cr_end_ts, idle_end_ts) - ts AS dur, 132 id, 133 root_id 134 FROM span_starts 135 WHERE 136 min(idle_end_ts, cr_end_ts) - ts > 0 137); 138 139-- Computes critical paths, the dependency graph of a task and returns a flattened view suitable 140-- for displaying in a UI track without any overlapping intervals. 141-- See the _critical_path MACRO above. 142-- 143-- Example usage on traces with Linux sched information: 144-- ``` 145-- -- Compute the userspace critical path from every task sleep. 146-- SELECT * FROM 147-- critical_path_intervals!( 148-- _wakeup_userspace_edges, 149-- (SELECT id AS root_node_id, prev_id - id FROM _wakeup_graph WHERE prev_id IS NOT NULL), 150-- _wakeup_intervals); 151-- ``` 152CREATE PERFETTO MACRO _critical_path_intervals( 153 -- A table/view/subquery corresponding to a directed graph on which the 154 -- reachability search should be performed. This table must have the columns 155 -- "source_node_id", "dest_node_id" and "edge_weight" corresponding to the two nodes on 156 -- either end of the edges in the graph and the edge weight. 157 -- 158 -- Note: the columns must contain uint32 similar to ids in trace processor 159 -- tables (i.e. the values should be relatively dense and close to zero). The 160 -- implementation makes assumptions on this for performance reasons and, if 161 -- this criteria is not, can lead to enormous amounts of memory being 162 -- allocated. 163 -- An edge weight is the absolute difference between the node ids forming the edge. 164 graph_table TableOrSubQuery, 165 -- A table/view/subquery corresponding to start nodes to |graph_table| which will be the 166 -- roots of the reachability trees. This table must have the columns 167 -- "root_node_id" and "capacity" corresponding to the starting node id and the capacity 168 -- of the root node to contain edge weights. 169 -- 170 -- Note: the columns must contain uint32 similar to ids in trace processor 171 -- tables (i.e. the values should be relatively dense and close to zero). The 172 -- implementation makes assumptions on this for performance reasons and, if 173 -- this criteria is not, can lead to enormous amounts of memory being 174 -- allocated. 175 root_table TableOrSubQuery, 176 -- A table/view/subquery corresponding to the idle to active transition points on a task. 177 -- This table must have the columns, "id", "ts", "dur" and "idle_dur". ts and dur is the 178 -- timestamp when the task became active and how long it was active for respectively. idle_dur 179 -- is the duration it was idle for before it became active at "ts". 180 -- 181 -- Note: the columns must contain uint32 similar to ids in trace processor 182 -- tables (i.e. the values should be relatively dense and close to zero). The 183 -- implementation makes assumptions on this for performance reasons and, if 184 -- this criteria is not, can lead to enormous amounts of memory being 185 -- allocated. 186 -- There should be one row for every node id encountered in the |graph_table|. 187 interval_table TableOrSubQuery 188) 189-- The returned table has the schema (id LONG, ts TIMESTAMP, dur DURATION, idle_dur LONG). 190-- |root_node_id| is the id of the starting node under which this edge was encountered. 191-- |node_id| is the id of the node from the input graph and |parent_node_id| 192-- is the id of the node which was the first encountered predecessor in a DFS 193-- search of the graph. 194RETURNS TableOrSubQuery AS 195( 196 WITH 197 _critical_path_nodes AS ( 198 SELECT 199 root_id, 200 id 201 FROM _critical_path!($graph_table, $root_table) 202 ) 203 SELECT 204 root_id, 205 id, 206 ts, 207 dur 208 FROM _critical_path_to_intervals !(_critical_path_nodes, $interval_table) 209 UNION ALL 210 SELECT 211 node.id AS root_id, 212 node.id, 213 node.ts, 214 node.dur 215 FROM $interval_table AS node 216 JOIN $root_table 217 ON root_node_id = id 218); 219