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