1-- 2-- Copyright 2023 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-- Compute the distribution of the overlap of the given intervals over time. 17-- 18-- Each interval is a (ts, dur) pair and the overlap represented as a (ts, value) 19-- counter, with the value corresponding to the number of intervals that overlap 20-- the given timestamp and interval until the next timestamp. 21CREATE PERFETTO MACRO intervals_overlap_count( 22 -- Table or subquery containing interval data. 23 segments TableOrSubquery, 24 -- Column containing interval starts (usually `ts`). 25 ts_column ColumnName, 26 -- Column containing interval durations (usually `dur`). 27 dur_column ColumnName) 28-- The returned table has the schema (ts INT64, value UINT32). 29-- |ts| is the timestamp when the number of open segments changed. |value| is 30-- the number of open segments. 31RETURNS TableOrSubquery AS 32( 33-- Algorithm: for each segment, emit a +1 at the start and a -1 at the end. 34-- Then, merge events with the same timestamp and compute a cumulative sum. 35WITH 36_starts AS ( 37 SELECT 38 1 AS delta, 39 $ts_column AS ts 40 FROM $segments 41), 42_ends AS ( 43 SELECT 44 -1 AS delta, 45 $ts_column + $dur_column AS ts 46 FROM $segments 47 WHERE $dur_column != -1 48), 49_events AS ( 50 SELECT * FROM _starts 51 UNION ALL 52 SELECT * FROM _ends 53), 54-- Merge events with the same timestamp to avoid artifacts in the data. 55_merged_events AS ( 56 SELECT ts, sum(delta) as delta 57 FROM _events 58 GROUP BY ts 59) 60SELECT 61 ts, 62 sum(delta) OVER ( 63 ORDER BY ts 64 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 65 ) as value 66FROM _merged_events 67ORDER BY ts 68); 69 70-- Returns whether |intervals| contains any overlapping intervals. Useful for 71-- checking if provided table/subquery can be used for intervals_intersect 72-- macro. 73CREATE PERFETTO MACRO _intervals_overlap_in_table( 74 -- Table/subquery of intervals with |ts| and |dur| columns. 75 intervals TableOrSubquery) 76-- Returns 1 if table contains overlapping intervals. Otherwise returns 0. 77RETURNS Expr AS ( 78WITH ts_with_next AS ( 79 SELECT 80 ts + dur AS ts_end, 81 -- The last slice will have |next_ts == NULL|, but it's not an issue as if 82 -- it's the last slice we know that it will not overlap with the next one. 83 LEAD(ts) OVER (ORDER BY ts) AS next_ts 84 FROM $intervals 85 WHERE dur != -1 86), filtered AS ( 87 SELECT * FROM ts_with_next 88 WHERE ts_end > next_ts 89 LIMIT 1 90) 91SELECT count() AS has_overlaps 92FROM filtered 93); 94 95-- Partition and flatten a hierarchy of intervals into non-overlapping intervals where 96-- each resulting interval is the leaf in the hierarchy at any given time. The result also 97-- denotes the 'self-time' of each interval. 98-- 99-- Each interval is a (root_id, id, parent_id, ts, dur) and the overlap is also represented as a 100-- (root_id, id, parent_id, ts, dur). 101-- Note that, children intervals must not be longer than any ancestor interval. 102CREATE PERFETTO MACRO _intervals_flatten( 103 -- Table or subquery containing all the root intervals: (id, ts, dur). Note that parent_id 104 -- is not necessary in this table as it will be NULL anyways. 105 roots_table TableOrSubquery, 106 -- Table or subquery containing all the child intervals. (root_id, id, parent_id, ts, dur) 107 children_table TableOrSubquery) 108RETURNS TableOrSubquery 109 AS ( 110 -- Algorithm: Sort all the start and end timestamps of the children within a root. 111 -- The interval duration between one timestamp and the next is one result. 112 -- If the timestamp is a start, the id is the id of the interval, if it's an end, 113 -- it's the parent_id. 114 -- Special case the edges of the roots and roots without children. 115 WITH 116 _roots AS ( 117 SELECT * FROM ($roots_table) WHERE dur > 0 118 ), 119 _children AS ( 120 SELECT * FROM ($children_table) WHERE dur > 0 121 ), 122 _roots_without_children AS ( 123 SELECT id FROM _roots 124 EXCEPT 125 SELECT DISTINCT parent_id AS id FROM _children 126 ), 127 _children_with_root_ts_and_dur AS ( 128 SELECT 129 _roots.id AS root_id, 130 _roots.ts AS root_ts, 131 _roots.dur AS root_dur, 132 _children.id, 133 _children.parent_id, 134 _children.ts, 135 _children.dur 136 FROM _children 137 JOIN _roots ON _roots.id = root_id 138 ), 139 _ends AS ( 140 SELECT 141 child.root_id, 142 child.root_ts, 143 child.root_dur, 144 IFNULL(parent.id, child.root_id) AS id, 145 parent.parent_id, 146 child.ts + child.dur AS ts 147 FROM _children_with_root_ts_and_dur child 148 LEFT JOIN _children_with_root_ts_and_dur parent 149 ON child.parent_id = parent.id 150 ), 151 _events AS ( 152 SELECT root_id, root_ts, root_dur, id, parent_id, ts FROM _children_with_root_ts_and_dur 153 UNION ALL 154 SELECT root_id, root_ts, root_dur, id, parent_id, ts FROM _ends 155 ), 156 _intervals AS ( 157 SELECT 158 root_id, 159 root_ts, 160 root_dur, 161 id, 162 parent_id, 163 ts, 164 LEAD(ts) 165 OVER (PARTITION BY root_id ORDER BY ts) - ts AS dur 166 FROM _events 167 ), 168 _only_middle AS ( 169 SELECT * FROM _intervals WHERE dur > 0 170 ), 171 _only_start AS ( 172 SELECT 173 root_id, 174 parent_id AS id, 175 NULL AS parent_id, 176 root_ts AS ts, 177 MIN(ts) - root_ts AS dur 178 FROM _only_middle 179 GROUP BY root_id 180 ), 181 _only_end AS ( 182 SELECT 183 root_id, 184 parent_id AS id, 185 NULL AS parent_id, 186 MAX(ts + dur) AS ts, 187 root_ts + root_dur - MAX(ts + dur) AS dur 188 FROM _only_middle 189 GROUP BY root_id 190 ), 191 _only_singleton AS ( 192 SELECT id AS root_id, id, NULL AS parent_id, ts, dur 193 FROM _roots 194 JOIN _roots_without_children USING (id) 195 ) 196 SELECT root_id, id, parent_id, ts, dur FROM _only_middle 197 UNION ALL 198 SELECT root_id, id, parent_id, ts, dur FROM _only_start 199 UNION ALL 200 SELECT root_id, id, parent_id, ts, dur FROM _only_end 201 UNION ALL 202 SELECT root_id, id, parent_id, ts, dur FROM _only_singleton 203); 204