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