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