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