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