• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 slices.flat_slices;
18
19INCLUDE PERFETTO MODULE sched.thread_executing_span;
20
21CREATE PERFETTO TABLE _critical_path_userspace AS
22SELECT
23  *
24FROM _critical_path_intervals
25    !(_wakeup_userspace_edges,
26      (SELECT id AS root_node_id, id - COALESCE(prev_id, id) AS capacity FROM _wakeup_graph),
27      _wakeup_intervals);
28
29CREATE PERFETTO TABLE _critical_path_kernel AS
30WITH
31  _kernel_nodes AS (
32    SELECT
33      id,
34      root_id
35    FROM _critical_path_userspace
36    JOIN _wakeup_graph
37      USING (id)
38    WHERE
39      is_idle_reason_self = 1
40  )
41SELECT
42  _kernel_nodes.root_id,
43  cr.root_id AS parent_id,
44  cr.id,
45  cr.ts,
46  cr.dur
47FROM _critical_path_intervals
48    !(_wakeup_kernel_edges,
49      (
50        SELECT graph.id AS root_node_id, graph.id - COALESCE(graph.prev_id, graph.id) AS capacity
51        FROM _kernel_nodes
52        JOIN _wakeup_graph graph USING(id)
53      ),
54      _wakeup_intervals) AS cr
55JOIN _kernel_nodes
56  ON _kernel_nodes.id = cr.root_id;
57
58CREATE PERFETTO TABLE _critical_path_userspace_adjusted AS
59SELECT DISTINCT
60  *
61FROM _critical_path_userspace_adjusted!(_critical_path_userspace, _wakeup_graph);
62
63CREATE PERFETTO TABLE _critical_path_kernel_adjusted AS
64SELECT DISTINCT
65  *
66FROM _critical_path_kernel_adjusted!(_critical_path_userspace_adjusted, _critical_path_kernel, _wakeup_graph);
67
68CREATE PERFETTO TABLE _critical_path_merged_adjusted AS
69SELECT
70  root_id,
71  parent_id,
72  id,
73  ts,
74  dur
75FROM _critical_path_userspace_adjusted
76UNION ALL
77SELECT
78  root_id,
79  parent_id,
80  id,
81  ts,
82  dur
83FROM _critical_path_kernel_adjusted
84WHERE
85  id != parent_id;
86
87CREATE PERFETTO TABLE _critical_path_roots AS
88SELECT
89  root_id,
90  min(ts) AS root_ts,
91  max(ts + dur) - min(ts) AS root_dur
92FROM _critical_path_userspace_adjusted
93GROUP BY
94  root_id;
95
96CREATE PERFETTO TABLE _critical_path_roots_and_merged AS
97WITH
98  roots_and_merged_critical_path AS (
99    SELECT
100      root_id,
101      root_ts,
102      root_dur,
103      parent_id,
104      id,
105      ts,
106      dur
107    FROM _critical_path_merged_adjusted
108    JOIN _critical_path_roots
109      USING (root_id)
110  )
111SELECT
112  flat.root_id,
113  flat.id,
114  flat.ts,
115  flat.dur
116FROM _intervals_flatten!(roots_and_merged_critical_path) AS flat
117WHERE
118  flat.dur > 0
119GROUP BY
120  flat.root_id,
121  flat.ts;
122
123CREATE PERFETTO TABLE _critical_path_all AS
124SELECT
125  row_number() OVER (ORDER BY cr.ts) AS id,
126  cr.ts,
127  cr.dur,
128  cr.ts + cr.dur AS ts_end,
129  id_graph.utid,
130  root_id_graph.utid AS root_utid
131FROM _critical_path_roots_and_merged AS cr
132JOIN _wakeup_graph AS id_graph
133  ON cr.id = id_graph.id
134JOIN _wakeup_graph AS root_id_graph
135  ON cr.root_id = root_id_graph.id
136ORDER BY
137  cr.ts;
138
139-- Limited thread_state view that will later be span joined with the |_thread_executing_span_graph|.
140CREATE PERFETTO VIEW _span_thread_state_view AS
141SELECT
142  id AS thread_state_id,
143  ts,
144  dur,
145  utid,
146  state,
147  blocked_function AS function,
148  io_wait,
149  cpu
150FROM thread_state;
151
152-- Limited slice_view that will later be span joined with the critical path.
153CREATE PERFETTO VIEW _span_slice_view AS
154SELECT
155  slice_id,
156  depth AS slice_depth,
157  name AS slice_name,
158  cast_int!(ts) AS ts,
159  cast_int!(dur) AS dur,
160  utid
161FROM _slice_flattened;
162
163-- thread state span joined with slice.
164CREATE VIRTUAL TABLE _span_thread_state_slice_sp USING SPAN_LEFT_JOIN (
165    _span_thread_state_view PARTITIONED utid,
166    _span_slice_view PARTITIONED utid);
167
168CREATE PERFETTO TABLE _span_thread_state_slice AS
169SELECT
170  row_number() OVER (ORDER BY ts) AS id,
171  ts,
172  dur,
173  ts + dur AS ts_end,
174  utid,
175  thread_state_id,
176  state,
177  function,
178  cpu,
179  io_wait,
180  slice_id,
181  slice_name,
182  slice_depth
183FROM _span_thread_state_slice_sp
184WHERE
185  dur > 0
186ORDER BY
187  ts;
188
189CREATE PERFETTO TABLE _critical_path_thread_state_slice_raw AS
190SELECT
191  id_0 AS cr_id,
192  id_1 AS th_id,
193  ts,
194  dur
195FROM _interval_intersect!((_critical_path_all, _span_thread_state_slice), (utid));
196
197CREATE PERFETTO TABLE _critical_path_thread_state_slice AS
198SELECT
199  raw.ts,
200  raw.dur,
201  cr.utid,
202  thread_state_id,
203  state,
204  function,
205  cpu,
206  io_wait,
207  slice_id,
208  slice_name,
209  slice_depth,
210  root_utid
211FROM _critical_path_thread_state_slice_raw AS raw
212JOIN _critical_path_all AS cr
213  ON cr.id = raw.cr_id
214JOIN _span_thread_state_slice AS th
215  ON th.id = raw.th_id;
216
217-- Flattened slices span joined with their thread_states. This contains the 'self' information
218-- without 'critical_path' (blocking) information.
219CREATE VIRTUAL TABLE _self_sp USING SPAN_LEFT_JOIN (thread_state PARTITIONED utid, _slice_flattened PARTITIONED utid);
220
221-- Limited view of |_self_sp|.
222CREATE PERFETTO VIEW _self_view AS
223SELECT
224  id AS self_thread_state_id,
225  slice_id AS self_slice_id,
226  ts,
227  dur,
228  utid AS root_utid,
229  state AS self_state,
230  blocked_function AS self_function,
231  cpu AS self_cpu,
232  io_wait AS self_io_wait,
233  name AS self_slice_name,
234  depth AS self_slice_depth
235FROM _self_sp;
236
237-- Self and critical path span join. This contains the union of the time intervals from the following:
238--  a. Self slice stack + thread_state.
239--  b. Critical path stack + thread_state.
240CREATE VIRTUAL TABLE _self_and_critical_path_sp USING SPAN_JOIN (
241    _self_view PARTITIONED root_utid,
242    _critical_path_thread_state_slice PARTITIONED root_utid);
243
244-- Returns a view of |_self_and_critical_path_sp| unpivoted over the following columns:
245-- self thread_state.
246-- self blocked_function (if one exists).
247-- self process_name (enabled with |enable_process_name|).
248-- self thread_name (enabled with |enable_thread_name|).
249-- self slice_stack (enabled with |enable_self_slice|).
250-- critical_path thread_state.
251-- critical_path process_name.
252-- critical_path thread_name.
253-- critical_path slice_stack (enabled with |enable_critical_path_slice|).
254-- running cpu (if one exists).
255-- A 'stack' is the group of resulting unpivoted rows sharing the same timestamp.
256CREATE PERFETTO FUNCTION _critical_path_stack(
257    root_utid JOINID(thread.id),
258    ts TIMESTAMP,
259    dur DURATION,
260    enable_process_name LONG,
261    enable_thread_name LONG,
262    enable_self_slice LONG,
263    enable_critical_path_slice LONG
264)
265RETURNS TABLE (
266  id LONG,
267  ts TIMESTAMP,
268  dur DURATION,
269  utid JOINID(thread.id),
270  stack_depth LONG,
271  name STRING,
272  table_name STRING,
273  root_utid JOINID(thread.id)
274) AS
275-- Spans filtered to the query time window and root_utid.
276-- This is a preliminary step that gets the start and end ts of all the rows
277-- so that we can chop the ends of each interval correctly if it overlaps with the query time interval.
278WITH
279  relevant_spans_starts AS (
280    SELECT
281      self_thread_state_id,
282      self_state,
283      self_slice_id,
284      self_slice_name,
285      self_slice_depth,
286      self_function,
287      self_io_wait,
288      thread_state_id,
289      state,
290      function,
291      io_wait,
292      slice_id,
293      slice_name,
294      slice_depth,
295      cpu,
296      utid,
297      max(ts, $ts) AS ts,
298      min(ts + dur, $ts + $dur) AS end_ts,
299      root_utid
300    FROM _self_and_critical_path_sp
301    WHERE
302      dur > 0 AND root_utid = $root_utid
303  ),
304  -- This is the final step that gets the |dur| of each span from the start and
305  -- and end ts of the previous step.
306  -- Now we manually unpivot the result with 3 key steps: 1) Self 2) Critical path 3) CPU
307  -- This CTE is heavily used throughout the entire function so materializing it is
308  -- very important.
309  relevant_spans AS MATERIALIZED (
310    SELECT
311      self_thread_state_id,
312      self_state,
313      self_slice_id,
314      self_slice_name,
315      self_slice_depth,
316      self_function,
317      self_io_wait,
318      thread_state_id,
319      state,
320      function,
321      io_wait,
322      slice_id,
323      slice_name,
324      slice_depth,
325      cpu,
326      utid,
327      ts,
328      end_ts - ts AS dur,
329      root_utid,
330      utid
331    FROM relevant_spans_starts
332    WHERE
333      dur > 0
334  ),
335  -- 1. Builds the 'self' stack of items as an ordered UNION ALL
336  self_stack AS MATERIALIZED (
337    -- Builds the self thread_state
338    SELECT
339      self_thread_state_id AS id,
340      ts,
341      dur,
342      root_utid AS utid,
343      0 AS stack_depth,
344      'thread_state: ' || self_state AS name,
345      'thread_state' AS table_name,
346      root_utid
347    FROM relevant_spans
348    UNION ALL
349    -- Builds the self kernel blocked_function
350    SELECT
351      self_thread_state_id AS id,
352      ts,
353      dur,
354      root_utid AS utid,
355      1 AS stack_depth,
356      iif(self_state GLOB 'R*', NULL, 'kernel function: ' || self_function) AS name,
357      'thread_state' AS table_name,
358      root_utid
359    FROM relevant_spans
360    UNION ALL
361    -- Builds the self kernel io_wait
362    SELECT
363      self_thread_state_id AS id,
364      ts,
365      dur,
366      root_utid AS utid,
367      2 AS stack_depth,
368      iif(self_state GLOB 'R*', NULL, 'io_wait: ' || self_io_wait) AS name,
369      'thread_state' AS table_name,
370      root_utid
371    FROM relevant_spans
372    UNION ALL
373    -- Builds the self process_name
374    SELECT
375      self_thread_state_id AS id,
376      ts,
377      dur,
378      thread.utid,
379      3 AS stack_depth,
380      iif($enable_process_name, 'process_name: ' || process.name, NULL) AS name,
381      'thread_state' AS table_name,
382      root_utid
383    FROM relevant_spans
384    LEFT JOIN thread
385      ON thread.utid = root_utid
386    LEFT JOIN process
387      USING (upid)
388    -- Builds the self thread_name
389    UNION ALL
390    SELECT
391      self_thread_state_id AS id,
392      ts,
393      dur,
394      thread.utid,
395      4 AS stack_depth,
396      iif($enable_thread_name, 'thread_name: ' || thread.name, NULL) AS name,
397      'thread_state' AS table_name,
398      root_utid
399    FROM relevant_spans
400    LEFT JOIN thread
401      ON thread.utid = root_utid
402    JOIN process
403      USING (upid)
404    UNION ALL
405    -- Builds the self 'ancestor' slice stack
406    SELECT
407      anc.id,
408      slice.ts,
409      slice.dur,
410      root_utid AS utid,
411      anc.depth + 5 AS stack_depth,
412      iif($enable_self_slice, anc.name, NULL) AS name,
413      'slice' AS table_name,
414      root_utid
415    FROM relevant_spans AS slice, ancestor_slice(self_slice_id) AS anc
416    WHERE
417      anc.dur != -1
418    UNION ALL
419    -- Builds the self 'deepest' ancestor slice stack
420    SELECT
421      self_slice_id AS id,
422      ts,
423      dur,
424      root_utid AS utid,
425      self_slice_depth + 5 AS stack_depth,
426      iif($enable_self_slice, self_slice_name, NULL) AS name,
427      'slice' AS table_name,
428      root_utid
429    FROM relevant_spans AS slice
430    ORDER BY
431      stack_depth
432  ),
433  -- Prepares for stage 2 in building the entire stack.
434  -- Computes the starting depth for each stack. This is necessary because
435  -- each self slice stack has variable depth and the depth in each stack
436  -- most be contiguous in order to efficiently generate a pprof in the future.
437  critical_path_start_depth AS MATERIALIZED (
438    SELECT
439      root_utid,
440      ts,
441      max(stack_depth) + 1 AS start_depth
442    FROM self_stack
443    GROUP BY
444      root_utid,
445      ts
446  ),
447  critical_path_span AS MATERIALIZED (
448    SELECT
449      thread_state_id,
450      state,
451      function,
452      io_wait,
453      slice_id,
454      slice_name,
455      slice_depth,
456      spans.ts,
457      spans.dur,
458      spans.root_utid,
459      utid,
460      start_depth
461    FROM relevant_spans AS spans
462    JOIN critical_path_start_depth
463      ON critical_path_start_depth.root_utid = spans.root_utid
464      AND critical_path_start_depth.ts = spans.ts
465    WHERE
466      critical_path_start_depth.root_utid = $root_utid
467      AND spans.root_utid != spans.utid
468  ),
469  -- 2. Builds the 'critical_path' stack of items as an ordered UNION ALL
470  critical_path_stack AS MATERIALIZED (
471    -- Builds the critical_path thread_state
472    SELECT
473      thread_state_id AS id,
474      ts,
475      dur,
476      utid,
477      start_depth AS stack_depth,
478      'blocking thread_state: ' || state AS name,
479      'thread_state' AS table_name,
480      root_utid
481    FROM critical_path_span
482    UNION ALL
483    -- Builds the critical_path process_name
484    SELECT
485      thread_state_id AS id,
486      ts,
487      dur,
488      thread.utid,
489      start_depth + 1 AS stack_depth,
490      'blocking process_name: ' || process.name,
491      'thread_state' AS table_name,
492      root_utid
493    FROM critical_path_span
494    JOIN thread
495      USING (utid)
496    LEFT JOIN process
497      USING (upid)
498    UNION ALL
499    -- Builds the critical_path thread_name
500    SELECT
501      thread_state_id AS id,
502      ts,
503      dur,
504      thread.utid,
505      start_depth + 2 AS stack_depth,
506      'blocking thread_name: ' || thread.name,
507      'thread_state' AS table_name,
508      root_utid
509    FROM critical_path_span
510    JOIN thread
511      USING (utid)
512    UNION ALL
513    -- Builds the critical_path kernel blocked_function
514    SELECT
515      thread_state_id AS id,
516      ts,
517      dur,
518      thread.utid,
519      start_depth + 3 AS stack_depth,
520      'blocking kernel_function: ' || function,
521      'thread_state' AS table_name,
522      root_utid
523    FROM critical_path_span
524    JOIN thread
525      USING (utid)
526    UNION ALL
527    -- Builds the critical_path kernel io_wait
528    SELECT
529      thread_state_id AS id,
530      ts,
531      dur,
532      thread.utid,
533      start_depth + 4 AS stack_depth,
534      'blocking io_wait: ' || io_wait,
535      'thread_state' AS table_name,
536      root_utid
537    FROM critical_path_span
538    JOIN thread
539      USING (utid)
540    UNION ALL
541    -- Builds the critical_path 'ancestor' slice stack
542    SELECT
543      anc.id,
544      slice.ts,
545      slice.dur,
546      slice.utid,
547      anc.depth + start_depth + 5 AS stack_depth,
548      iif($enable_critical_path_slice, anc.name, NULL) AS name,
549      'slice' AS table_name,
550      root_utid
551    FROM critical_path_span AS slice, ancestor_slice(slice_id) AS anc
552    WHERE
553      anc.dur != -1
554    UNION ALL
555    -- Builds the critical_path 'deepest' slice
556    SELECT
557      slice_id AS id,
558      ts,
559      dur,
560      utid,
561      slice_depth + start_depth + 5 AS stack_depth,
562      iif($enable_critical_path_slice, slice_name, NULL) AS name,
563      'slice' AS table_name,
564      root_utid
565    FROM critical_path_span AS slice
566    ORDER BY
567      stack_depth
568  ),
569  -- Prepares for stage 3 in building the entire stack.
570  -- Computes the starting depth for each stack using the deepest stack_depth between
571  -- the critical_path stack and self stack. The self stack depth is
572  -- already computed and materialized in |critical_path_start_depth|.
573  cpu_start_depth_raw AS (
574    SELECT
575      root_utid,
576      ts,
577      max(stack_depth) + 1 AS start_depth
578    FROM critical_path_stack
579    GROUP BY
580      root_utid,
581      ts
582    UNION ALL
583    SELECT
584      *
585    FROM critical_path_start_depth
586  ),
587  cpu_start_depth AS (
588    SELECT
589      root_utid,
590      ts,
591      max(start_depth) AS start_depth
592    FROM cpu_start_depth_raw
593    GROUP BY
594      root_utid,
595      ts
596  ),
597  -- 3. Builds the 'CPU' stack for 'Running' states in either the self or critical path stack.
598  cpu_stack AS (
599    SELECT
600      thread_state_id AS id,
601      spans.ts,
602      spans.dur,
603      utid,
604      start_depth AS stack_depth,
605      'cpu: ' || cpu AS name,
606      'thread_state' AS table_name,
607      spans.root_utid
608    FROM relevant_spans AS spans
609    JOIN cpu_start_depth
610      ON cpu_start_depth.root_utid = spans.root_utid AND cpu_start_depth.ts = spans.ts
611    WHERE
612      cpu_start_depth.root_utid = $root_utid
613      AND state = 'Running'
614      OR self_state = 'Running'
615  ),
616  merged AS (
617    SELECT
618      *
619    FROM self_stack
620    UNION ALL
621    SELECT
622      *
623    FROM critical_path_stack
624    UNION ALL
625    SELECT
626      *
627    FROM cpu_stack
628  )
629SELECT
630  *
631FROM merged
632WHERE
633  id IS NOT NULL;
634
635-- Critical path stack of thread_executing_spans with the following entities in the critical path
636-- stacked from top to bottom: self thread_state, self blocked_function, self process_name,
637-- self thread_name, slice stack, critical_path thread_state, critical_path process_name,
638-- critical_path thread_name, critical_path slice_stack, running_cpu.
639CREATE PERFETTO FUNCTION _thread_executing_span_critical_path_stack(
640    -- Thread utid to filter critical paths to.
641    root_utid JOINID(thread.id),
642    -- Timestamp of start of time range to filter critical paths to.
643    ts TIMESTAMP,
644    -- Duration of time range to filter critical paths to.
645    dur DURATION
646)
647RETURNS TABLE (
648  -- Id of the thread_state or slice in the thread_executing_span.
649  id LONG,
650  -- Timestamp of slice in the critical path.
651  ts TIMESTAMP,
652  -- Duration of slice in the critical path.
653  dur DURATION,
654  -- Utid of thread that emitted the slice.
655  utid JOINID(thread.id),
656  -- Stack depth of the entitity in the debug track.
657  stack_depth LONG,
658  -- Name of entity in the critical path (could be a thread_state, kernel blocked_function, process_name, thread_name, slice name or cpu).
659  name STRING,
660  -- Table name of entity in the critical path (could be either slice or thread_state).
661  table_name STRING,
662  -- Utid of the thread the critical path was filtered to.
663  root_utid JOINID(thread.id)
664) AS
665SELECT
666  *
667FROM _critical_path_stack($root_utid, $ts, $dur, 1, 1, 1, 1);
668
669-- Returns a pprof aggregation of the stacks in |_critical_path_stack|.
670CREATE PERFETTO FUNCTION _critical_path_graph(
671    graph_title STRING,
672    root_utid JOINID(thread.id),
673    ts TIMESTAMP,
674    dur DURATION,
675    enable_process_name LONG,
676    enable_thread_name LONG,
677    enable_self_slice LONG,
678    enable_critical_path_slice LONG
679)
680RETURNS TABLE (
681  pprof BYTES
682) AS
683WITH
684  stack AS MATERIALIZED (
685    SELECT
686      ts,
687      dur - coalesce(lead(dur) OVER (PARTITION BY root_utid, ts ORDER BY stack_depth), 0) AS dur,
688      name,
689      utid,
690      root_utid,
691      stack_depth
692    FROM _critical_path_stack(
693      $root_utid,
694      $ts,
695      $dur,
696      $enable_process_name,
697      $enable_thread_name,
698      $enable_self_slice,
699      $enable_critical_path_slice
700    )
701  ),
702  graph AS (
703    SELECT
704      cat_stacks($graph_title) AS stack
705  ),
706  parent AS (
707    SELECT
708      cr.ts,
709      cr.dur,
710      cr.name,
711      cr.utid,
712      cr.stack_depth,
713      cat_stacks(graph.stack, cr.name) AS stack,
714      cr.root_utid
715    FROM stack AS cr, graph
716    WHERE
717      stack_depth = 0
718    UNION ALL
719    SELECT
720      child.ts,
721      child.dur,
722      child.name,
723      child.utid,
724      child.stack_depth,
725      cat_stacks(stack, child.name) AS stack,
726      child.root_utid
727    FROM stack AS child
728    JOIN parent
729      ON parent.root_utid = child.root_utid
730      AND parent.ts = child.ts
731      AND child.stack_depth = parent.stack_depth + 1
732  ),
733  stacks AS (
734    SELECT
735      dur,
736      stack
737    FROM parent
738  )
739SELECT
740  experimental_profile(stack, 'duration', 'ns', dur) AS pprof
741FROM stacks;
742
743-- Returns a pprof aggreagation of the stacks in |_thread_executing_span_critical_path_stack|
744CREATE PERFETTO FUNCTION _thread_executing_span_critical_path_graph(
745    -- Descriptive name for the graph.
746    graph_title STRING,
747    -- Thread utid to filter critical paths to.
748    root_utid JOINID(thread.id),
749    -- Timestamp of start of time range to filter critical paths to.
750    ts TIMESTAMP,
751    -- Duration of time range to filter critical paths to.
752    dur DURATION
753)
754RETURNS TABLE (
755  -- Pprof of critical path stacks.
756  pprof BYTES
757) AS
758SELECT
759  *
760FROM _critical_path_graph($graph_title, $root_utid, $ts, $dur, 1, 1, 1, 1);
761