xref: /aosp_15_r20/external/perfetto/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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
17INCLUDE PERFETTO MODULE graphs.critical_path;
18INCLUDE PERFETTO MODULE graphs.search;
19INCLUDE PERFETTO MODULE intervals.overlap;
20INCLUDE PERFETTO MODULE intervals.intersect;
21
22-- A 'thread_executing_span' is thread_state span starting with a runnable slice
23-- until the next runnable slice that's woken up by a process (as opposed
24-- to an interrupt). Note that within a 'thread_executing_span' we can have sleep
25-- spans blocked on an interrupt.
26-- We consider the id of this span to be the id of the first thread_state in the span.
27
28--
29-- Finds all runnable states that are woken up by a process.
30--
31-- We achieve this by checking that the |thread_state.irq_context|
32-- value is NOT 1. In otherwords, it is either 0 or NULL. The NULL check
33-- is important to support older Android versions.
34--
35-- On older versions of Android (<U). We don't have IRQ context information,
36-- so this table might contain wakeups from interrupt context, consequently, the
37-- wakeup graph generated might not be accurate.
38--
39CREATE PERFETTO TABLE _runnable_state
40AS
41SELECT
42  thread_state.id,
43  thread_state.ts,
44  thread_state.dur,
45  thread_state.state,
46  thread_state.utid,
47  thread_state.waker_id,
48  thread_state.waker_utid,
49  IIF(thread_state.irq_context = 0 OR thread_state.irq_context IS NULL,
50      IFNULL(thread_state.io_wait, 0), 1) AS is_irq
51FROM thread_state
52WHERE
53  thread_state.dur != -1
54  AND thread_state.waker_id IS NOT NULL;
55
56-- Similar to |_runnable_state| but finds the first runnable state at thread.
57CREATE PERFETTO TABLE _first_runnable_state
58AS
59WITH
60  first_state AS (
61    SELECT
62      MIN(thread_state.id) AS id
63    FROM thread_state
64    GROUP BY utid
65  )
66SELECT
67  thread_state.id,
68  thread_state.ts,
69  thread_state.dur,
70  thread_state.state,
71  thread_state.utid,
72  thread_state.waker_id,
73  thread_state.waker_utid,
74  IIF(thread_state.irq_context = 0 OR thread_state.irq_context IS NULL,
75      IFNULL(thread_state.io_wait, 0), 1) AS is_irq
76FROM thread_state
77JOIN first_state
78  USING (id)
79WHERE
80  thread_state.dur != -1
81  AND thread_state.state = 'R';
82
83--
84-- Finds all sleep states including interruptible (S) and uninterruptible (D).
85CREATE PERFETTO TABLE _sleep_state
86AS
87SELECT
88  thread_state.id,
89  thread_state.ts,
90  thread_state.dur,
91  thread_state.state,
92  thread_state.blocked_function,
93  thread_state.utid
94FROM thread_state
95WHERE dur != -1 AND (state = 'S' OR state = 'D' OR state = 'I');
96
97--
98-- Finds the last execution for every thread to end executing_spans without a Sleep.
99--
100CREATE PERFETTO TABLE _thread_end_ts
101AS
102SELECT
103  MAX(ts) + dur AS end_ts,
104  utid
105FROM thread_state
106WHERE dur != -1
107GROUP BY utid;
108
109-- Similar to |_sleep_state| but finds the first sleep state in a thread.
110CREATE PERFETTO TABLE _first_sleep_state
111AS
112SELECT
113  MIN(s.id) AS id,
114  s.ts,
115  s.dur,
116  s.state,
117  s.blocked_function,
118  s.utid
119FROM _sleep_state s
120JOIN _runnable_state r
121  ON s.utid = r.utid AND (s.ts + s.dur = r.ts)
122GROUP BY s.utid;
123
124--
125-- Finds all neighbouring ('Sleeping', 'Runnable') thread_states pairs from the same thread.
126-- More succintly, pairs of S[n-1]-R[n] where R is woken by a process context and S is an
127-- interruptible or uninterruptible sleep state.
128--
129-- This is achieved by joining the |_runnable_state|.ts with the
130-- |_sleep_state|.|ts + dur|.
131--
132-- With the S-R pairs of a thread, we can re-align to [R-S) intervals with LEADS and LAGS.
133--
134-- Given the following thread_states on a thread:
135-- S0__|R0__Running0___|S1__|R1__Running1___|S2__|R2__Running2__S2|.
136--
137-- We have 3 thread_executing_spans: [R0, S0), [R1, S1), [R2, S2).
138--
139-- We define the following markers in this table:
140--
141-- prev_id          = R0_id.
142--
143-- prev_end_ts      = S0_ts.
144-- state            = 'S' or 'D'.
145-- blocked_function = <kernel blocking function>
146--
147-- id               = R1_id.
148-- ts               = R1_ts.
149--
150-- end_ts           = S1_ts.
151CREATE PERFETTO TABLE _wakeup
152AS
153WITH
154  all_wakeups AS (
155    SELECT
156      s.state,
157      s.blocked_function,
158      r.id,
159      r.ts AS ts,
160      r.utid AS utid,
161      r.waker_id,
162      r.waker_utid,
163      s.ts AS prev_end_ts,
164      is_irq
165    FROM _runnable_state r
166    JOIN _sleep_state s
167      ON s.utid = r.utid AND (s.ts + s.dur = r.ts)
168    UNION ALL
169    SELECT
170      NULL AS state,
171      NULL AS blocked_function,
172      r.id,
173      r.ts,
174      r.utid AS utid,
175      r.waker_id,
176      r.waker_utid,
177      NULL AS prev_end_ts,
178      is_irq
179    FROM _first_runnable_state r
180    LEFT JOIN _first_sleep_state s
181      ON s.utid = r.utid
182  )
183SELECT
184  all_wakeups.*, thread_end.end_ts AS thread_end_ts
185FROM all_wakeups
186LEFT JOIN _thread_end_ts thread_end
187  USING (utid);
188
189-- Mapping from running thread state to runnable
190-- TODO(zezeozue): Switch to use `sched_previous_runnable_on_thread`.
191CREATE PERFETTO TABLE _wakeup_map
192AS
193WITH x AS (
194SELECT id, waker_id, utid, state FROM thread_state WHERE state = 'Running' AND dur != -1
195UNION ALL
196SELECT id, waker_id, utid, state FROM _first_runnable_state
197UNION ALL
198SELECT id, waker_id, utid, state FROM _runnable_state
199), y AS (
200    SELECT
201      id AS waker_id,
202      state,
203      MAX(id)
204        filter(WHERE state = 'R')
205          OVER (PARTITION BY utid ORDER BY id) AS id
206    FROM x
207  )
208SELECT id, waker_id FROM y WHERE state = 'Running' ORDER BY waker_id;
209
210--
211-- Builds the waker and prev relationships for all thread_executing_spans.
212--
213CREATE PERFETTO TABLE _wakeup_graph
214AS
215WITH
216  _wakeup_events AS (
217    SELECT
218      utid,
219      thread_end_ts,
220      IIF(is_irq, 'IRQ', state) AS idle_state,
221      blocked_function AS idle_reason,
222      _wakeup.id,
223      IIF(is_irq, NULL, _wakeup_map.id) AS waker_id,
224      _wakeup.ts,
225      prev_end_ts AS idle_ts,
226      IIF(is_irq OR _wakeup_map.id IS NULL OR (state IS NOT NULL AND state != 'S'), 1, 0)
227        AS is_idle_reason_self
228    FROM _wakeup
229    LEFT JOIN _wakeup_map
230      USING (waker_id)
231  )
232SELECT
233  utid,
234  id,
235  waker_id,
236  ts,
237  idle_state,
238  idle_reason,
239  ts - idle_ts AS idle_dur,
240  is_idle_reason_self,
241  LAG(id) OVER (PARTITION BY utid ORDER BY ts) AS prev_id,
242  LEAD(id) OVER (PARTITION BY utid ORDER BY ts) AS next_id,
243  IFNULL(LEAD(idle_ts) OVER (PARTITION BY utid ORDER BY ts), thread_end_ts) - ts AS dur,
244  LEAD(is_idle_reason_self) OVER (PARTITION BY utid ORDER BY ts) AS is_next_idle_reason_self
245FROM _wakeup_events
246ORDER BY id;
247
248-- View of all the edges for the userspace critical path.
249CREATE PERFETTO VIEW _wakeup_userspace_edges
250AS
251SELECT
252  id AS source_node_id,
253  COALESCE(IIF(is_idle_reason_self, prev_id, waker_id), id) AS dest_node_id,
254  id - COALESCE(IIF(is_idle_reason_self, prev_id, waker_id), id) AS edge_weight
255FROM _wakeup_graph;
256
257-- View of all the edges for the kernel critical path.
258CREATE PERFETTO VIEW _wakeup_kernel_edges
259AS
260SELECT
261  id AS source_node_id,
262  COALESCE(waker_id, id) AS dest_node_id,
263  id - COALESCE(waker_id, id) AS edge_weight
264FROM _wakeup_graph;
265
266-- View of the relevant timestamp and intervals for all nodes in the critical path.
267CREATE PERFETTO VIEW _wakeup_intervals
268AS
269SELECT id, ts, dur, idle_dur FROM _wakeup_graph;
270
271-- Converts a table with <ts, dur, utid> columns to a unique set of wakeup roots <id> that
272-- completely cover the time intervals.
273CREATE PERFETTO MACRO _intervals_to_roots(_source_table TableOrSubQuery,
274                                          _node_table TableOrSubQuery)
275RETURNS TableOrSubQuery
276AS (
277  WITH _interval_to_root_nodes AS (
278      SELECT * FROM $_node_table
279    ),
280    _source AS (
281      SELECT * FROM $_source_table
282    ),
283    _thread_bounds AS (
284      SELECT utid, MIN(ts) AS min_start, MAX(ts) AS max_start
285      FROM _interval_to_root_nodes
286      GROUP BY utid
287    ),
288    _start AS (
289      SELECT
290        _interval_to_root_nodes.utid,
291        MAX(_interval_to_root_nodes.id) AS _start_id,
292        _source.ts,
293        _source.dur
294      FROM _interval_to_root_nodes
295      JOIN _thread_bounds USING (utid)
296      JOIN _source
297        ON _source.utid = _interval_to_root_nodes.utid
298          AND MAX(_source.ts, min_start) >= _interval_to_root_nodes.ts
299      GROUP BY _source.ts, _source.utid
300    ),
301    _end AS (
302      SELECT
303        _interval_to_root_nodes.utid,
304        MIN(_interval_to_root_nodes.id) AS _end_id,
305        _source.ts,
306        _source.dur
307      FROM _interval_to_root_nodes
308      JOIN _thread_bounds USING (utid)
309      JOIN _source
310        ON _source.utid = _interval_to_root_nodes.utid
311          AND MIN((_source.ts + _source.dur), max_start) <= _interval_to_root_nodes.ts
312      GROUP BY _source.ts, _source.utid
313    ),
314    _bound AS (
315      SELECT _start.utid, _start.ts, _start.dur, _start_id, _end_id
316      FROM _start
317      JOIN _end
318        ON _start.ts = _end.ts AND _start.dur = _end.dur AND _start.utid = _end.utid
319    )
320  SELECT DISTINCT id AS root_node_id, id - COALESCE(prev_id, id) AS capacity
321  FROM _bound
322  JOIN _interval_to_root_nodes
323    ON _interval_to_root_nodes.id BETWEEN _start_id AND _end_id
324      AND _interval_to_root_nodes.utid = _bound.utid
325);
326
327-- Adjusts the userspace critical path such that any interval that includes a kernel stall
328-- gets the next id, the root id of the kernel critical path. This ensures that the merge
329-- step associates the userspace critical path and kernel critical path on the same interval
330-- correctly.
331CREATE PERFETTO MACRO _critical_path_userspace_adjusted(_critical_path_table TableOrSubQuery,
332                                                        _node_table TableOrSubQuery)
333RETURNS TableOrSubQuery
334AS (
335    SELECT
336      cr.root_id,
337      cr.root_id AS parent_id,
338      IIF(node.is_next_idle_reason_self, node.next_id, cr.id) AS id,
339      cr.ts,
340      cr.dur
341    FROM (SELECT * FROM $_critical_path_table) cr
342    JOIN $_node_table node
343      USING (id)
344);
345
346-- Adjusts the start and end of the kernel critical path such that it is completely bounded within
347-- its corresponding userspace critical path.
348CREATE PERFETTO MACRO _critical_path_kernel_adjusted(_userspace_critical_path_table TableOrSubQuery,
349                                                     _kernel_critical_path_table TableOrSubQuery,
350                                                     _node_table TableOrSubQuery)
351RETURNS TableOrSubQuery
352AS (
353    SELECT
354      kernel_cr.root_id,
355      kernel_cr.root_id AS parent_id,
356      kernel_cr.id,
357      MAX(kernel_cr.ts, userspace_cr.ts) AS ts,
358      MIN(kernel_cr.ts + kernel_cr.dur, userspace_cr.ts + userspace_cr.dur)
359        - MAX(kernel_cr.ts, userspace_cr.ts) AS dur
360    FROM $_kernel_critical_path_table kernel_cr
361    JOIN $_node_table node
362      ON kernel_cr.parent_id = node.id
363    JOIN $_userspace_critical_path_table userspace_cr
364      ON userspace_cr.id = kernel_cr.parent_id AND userspace_cr.root_id = kernel_cr.root_id
365);
366
367-- Merge the kernel and userspace critical path such that the corresponding kernel critical path
368-- has priority over userpsace critical path it overlaps.
369CREATE PERFETTO MACRO _critical_path_merged(_userspace_critical_path_table TableOrSubQuery,
370                                            _kernel_critical_path_table TableOrSubQuery,
371                                            _node_table TableOrSubQuery)
372RETURNS TableOrSubQuery
373AS (
374WITH _userspace_critical_path AS (
375  SELECT DISTINCT *
376  FROM _critical_path_userspace_adjusted!(
377    $_userspace_critical_path_table,
378    $_node_table)
379  ),
380  _merged_critical_path AS (
381    SELECT * FROM _userspace_critical_path
382     UNION ALL
383    SELECT DISTINCT *
384    FROM _critical_path_kernel_adjusted!(
385      _userspace_critical_path,
386      $_kernel_critical_path_table,
387      $_node_table)
388    WHERE id != parent_id
389    ),
390    _roots_critical_path AS (
391      SELECT root_id, MIN(ts) AS root_ts, MAX(ts + dur) - MIN(ts) AS root_dur
392      FROM _userspace_critical_path
393      GROUP BY root_id
394    ),
395    _roots_and_merged_critical_path AS (
396      SELECT
397        root_id,
398        root_ts,
399        root_dur,
400        parent_id,
401        id,
402        ts,
403        dur
404      FROM _merged_critical_path
405      JOIN _roots_critical_path USING(root_id)
406    )
407    SELECT
408      flat.root_id,
409      flat.id,
410      flat.ts,
411      flat.dur
412    FROM
413    _intervals_flatten!(_roots_and_merged_critical_path) flat
414    WHERE flat.dur > 0
415    GROUP BY flat.root_id, flat.ts
416);
417
418-- Generates the critical path for only the set of roots <id> passed in.
419-- _intervals_to_roots can be used to generate root ids from a given time interval.
420-- This can be used to genrate the critical path over sparse regions of a trace, e.g
421-- binder transactions. It might be more efficient to generate the _critical_path
422-- for the entire trace, see _thread_executing_span_critical_path_all, but for a
423-- per-process susbset of binder txns for instance, this is likely faster.
424CREATE PERFETTO MACRO _critical_path_by_roots(_roots_table TableOrSubQuery,
425                                              _node_table TableOrSubQuery)
426RETURNS TableOrSubQuery
427AS (
428  WITH _userspace_critical_path_by_roots AS (
429    SELECT *
430    FROM
431      _critical_path_intervals
432        !(_wakeup_userspace_edges,
433          $_roots_table,
434          _wakeup_intervals)
435  ),
436  _kernel_nodes AS (
437    SELECT id, root_id FROM _userspace_critical_path_by_roots
438    JOIN $_node_table node USING (id) WHERE is_idle_reason_self = 1
439  ),
440  _kernel_critical_path_by_roots AS (
441    SELECT _kernel_nodes.root_id, cr.root_id AS parent_id, cr.id, cr.ts, cr.dur
442    FROM
443      _critical_path_intervals
444        !(_wakeup_kernel_edges,
445          (
446           SELECT graph.id AS root_node_id, graph.id - COALESCE(graph.prev_id, graph.id) AS capacity
447           FROM _kernel_nodes
448           JOIN _wakeup_graph graph USING(id)
449          ),
450          _wakeup_intervals)
451          cr
452    JOIN _kernel_nodes
453      ON _kernel_nodes.id = cr.root_id
454  ) SELECT * FROM _critical_path_merged!(
455    _userspace_critical_path_by_roots,
456    _kernel_critical_path_by_roots,
457    $_node_table)
458);
459
460-- Generates the critical path for only the time intervals for the utids given.
461-- Currently expensive because of naive interval_intersect implementation.
462-- Prefer _critical_paths_by_roots for performance. This is useful for a small
463-- set of intervals, e.g app startups in a trace.
464CREATE PERFETTO MACRO _critical_path_by_intervals(_intervals_table TableOrSubQuery,
465                                                  _node_table TableOrSubQuery)
466RETURNS TableOrSubQuery AS (
467  WITH _nodes AS (
468    SELECT * FROM $_node_table
469  ), _intervals AS (
470    SELECT
471      ROW_NUMBER() OVER(ORDER BY ts) AS id,
472      ts,
473      dur,
474      utid AS root_utid
475    FROM $_intervals_table
476  ), _critical_path AS (
477    SELECT
478      ROW_NUMBER() OVER(ORDER BY ts) AS id,
479      root_id,
480      id AS cr_id,
481      ts,
482      dur
483    FROM _critical_path_by_roots!(
484      _intervals_to_roots!($_intervals_table, $_node_table),
485      _nodes)
486  ), _span AS (
487    SELECT
488      _root_nodes.utid AS root_utid,
489      _nodes.utid,
490      cr.root_id,
491      cr.cr_id,
492      cr.id,
493      cr.ts,
494      cr.dur
495    FROM _critical_path cr
496    JOIN _nodes _root_nodes ON _root_nodes.id = cr.root_id
497    JOIN _nodes ON _nodes.id = cr.cr_id
498  ) SELECT DISTINCT
499      _span.root_utid,
500      _span.utid,
501      _span.root_id,
502      _span.cr_id AS id,
503      ii.ts,
504      ii.dur,
505      _intervals.ts AS interval_ts,
506      _intervals.dur AS interval_dur
507    FROM _interval_intersect!((_span, _intervals), (root_utid)) ii
508    JOIN _span ON _span.id = ii.id_0
509    JOIN _intervals ON _intervals.id = ii.id_1
510);
511
512-- Generates the critical path for a given utid over the <ts, dur> interval.
513-- The duration of a thread executing span in the critical path is the range between the
514-- start of the thread_executing_span and the start of the next span in the critical path.
515CREATE PERFETTO FUNCTION _thread_executing_span_critical_path(
516  -- Utid of the thread to compute the critical path for.
517  root_utid JOINID(thread.id),
518  -- Timestamp.
519  ts TIMESTAMP,
520  -- Duration.
521  dur DURATION)
522RETURNS TABLE(
523  -- Thread Utid the critical path was filtered to.
524  root_utid JOINID(thread.id),
525  -- Id of thread executing span following the sleeping thread state for which the critical path is
526  -- computed.
527  root_id LONG,
528  -- Id of the first (runnable) thread state in thread_executing_span.
529  id LONG,
530  -- Timestamp of first thread_state in thread_executing_span.
531  ts TIMESTAMP,
532  -- Duration of thread_executing_span.
533  dur DURATION,
534  -- Utid of thread with thread_state.
535  utid JOINID(thread.id)
536) AS
537SELECT root_utid, root_id, id, ts, dur, utid FROM _critical_path_by_intervals!(
538  (SELECT $root_utid AS utid, $ts as ts, $dur AS dur),
539  _wakeup_graph);
540