• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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