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