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 android.startup.startups; 18 19INCLUDE PERFETTO MODULE intervals.intersect; 20 21INCLUDE PERFETTO MODULE slices.with_context; 22 23-- Collect all GC slices. There's typically one enclosing slice but sometimes the 24-- CompactionPhase is outside the nesting and we need to include that. 25CREATE PERFETTO VIEW _gc_slice AS 26WITH 27 concurrent AS ( 28 SELECT 29 id AS gc_id, 30 name AS gc_name, 31 lead(name) OVER (PARTITION BY track_id ORDER BY ts) AS compact_name, 32 lead(dur) OVER (PARTITION BY track_id ORDER BY ts) AS compact_dur, 33 ts AS gc_ts, 34 iif(dur = -1, trace_end() - slice.ts, slice.dur) AS gc_dur, 35 ts, 36 dur, 37 tid, 38 utid, 39 pid, 40 upid, 41 thread_name, 42 process_name 43 FROM thread_slice AS slice 44 WHERE 45 depth = 0 46 ) 47SELECT 48 gc_id, 49 gc_name, 50 ts AS gc_ts, 51 ts, 52 gc_dur + iif( 53 compact_name = 'CompactionPhase' 54 OR compact_name = 'Background concurrent copying GC', 55 compact_dur, 56 0 57 ) AS gc_dur, 58 gc_dur + iif( 59 compact_name = 'CompactionPhase' 60 OR compact_name = 'Background concurrent copying GC', 61 compact_dur, 62 0 63 ) AS dur, 64 utid, 65 tid, 66 upid, 67 pid, 68 thread_name, 69 process_name 70FROM concurrent 71WHERE 72 gc_name GLOB '*concurrent*GC'; 73 74-- Extract the heap counter into <ts, dur, upid> 75CREATE PERFETTO VIEW _gc_heap_counter AS 76SELECT 77 c.ts, 78 coalesce(lead(c.ts) OVER (PARTITION BY track_id ORDER BY c.ts), trace_end()) - ts AS dur, 79 process.upid, 80 cast_int!(c.value ) AS value 81FROM counter AS c 82JOIN process_counter_track AS t 83 ON c.track_id = t.id 84INNER JOIN process 85 USING (upid) 86WHERE 87 t.name = 'Heap size (KB)'; 88 89-- Find the last heap counter after the GC slice dur. This is the best effort to find the 90-- final heap size after GC. The algorithm is like so: 91-- 1. Merge end_ts of the GC events with the start_ts of the heap counters. 92-- 2. Find the heap counter value right after each GC event. 93CREATE PERFETTO VIEW _gc_slice_with_final_heap AS 94WITH 95 slice_and_heap AS ( 96 SELECT 97 upid, 98 gc_id, 99 gc_ts + gc_dur AS ts, 100 NULL AS value 101 FROM _gc_slice 102 UNION ALL 103 SELECT 104 upid, 105 NULL AS gc_id, 106 ts, 107 value 108 FROM _gc_heap_counter 109 ), 110 next_heap AS ( 111 SELECT 112 *, 113 lead(value) OVER (PARTITION BY upid ORDER BY ts) AS last_value 114 FROM slice_and_heap 115 ), 116 slice_with_last_heap AS ( 117 SELECT 118 * 119 FROM next_heap 120 WHERE 121 gc_id IS NOT NULL 122 ) 123SELECT 124 _gc_slice.*, 125 last_value 126FROM _gc_slice 127LEFT JOIN slice_with_last_heap 128 USING (gc_id); 129 130-- Span join with all the other heap counters to find the overall min and max heap size. 131CREATE VIRTUAL TABLE _gc_slice_heap_sp USING SPAN_JOIN (_gc_slice_with_final_heap PARTITIONED upid, _gc_heap_counter PARTITIONED upid); 132 133-- Aggregate the min and max heap across the GC event, taking into account the last heap size 134-- derived earlier. 135CREATE PERFETTO TABLE _gc_slice_heap AS 136SELECT 137 gc_ts AS ts, 138 gc_dur AS dur, 139 upid, 140 gc_id, 141 gc_name, 142 gc_ts, 143 gc_dur, 144 utid, 145 tid, 146 pid, 147 thread_name, 148 process_name, 149 last_value, 150 value, 151 CASE 152 WHEN gc_name GLOB '*young*' 153 THEN 'young' 154 WHEN gc_name GLOB '*NativeAlloc*' 155 THEN 'native_alloc' 156 WHEN gc_name GLOB '*Alloc*' 157 THEN 'alloc' 158 WHEN gc_name GLOB '*CollectorTransition*' 159 THEN 'collector_transition' 160 WHEN gc_name GLOB '*Explicit*' 161 THEN 'explicit' 162 ELSE 'full' 163 END AS gc_type, 164 iif(gc_name GLOB '*mark compact*', 1, 0) AS is_mark_compact, 165 max(max(value, last_value)) / 1e3 AS max_heap_mb, 166 min(min(value, last_value)) / 1e3 AS min_heap_mb 167FROM _gc_slice_heap_sp 168GROUP BY 169 gc_id; 170 171-- Span join GC events with thread states to breakdown the time spent. 172CREATE VIRTUAL TABLE _gc_slice_heap_thread_state_sp USING SPAN_LEFT_JOIN (_gc_slice_heap PARTITIONED utid, thread_state PARTITIONED utid); 173 174-- All Garbage collection events with a breakdown of the time spent and heap reclaimed. 175CREATE PERFETTO TABLE android_garbage_collection_events ( 176 -- Tid of thread running garbage collection. 177 tid LONG, 178 -- Pid of process running garbage collection. 179 pid LONG, 180 -- Utid of thread running garbage collection. 181 utid JOINID(thread.id), 182 -- Upid of process running garbage collection. 183 upid JOINID(process.id), 184 -- Name of thread running garbage collection. 185 thread_name STRING, 186 -- Name of process running garbage collection. 187 process_name STRING, 188 -- Type of garbage collection. 189 gc_type STRING, 190 -- Whether gargage collection is mark compact or copying. 191 is_mark_compact LONG, 192 -- MB reclaimed after garbage collection. 193 reclaimed_mb DOUBLE, 194 -- Minimum heap size in MB during garbage collection. 195 min_heap_mb DOUBLE, 196 -- Maximum heap size in MB during garbage collection. 197 max_heap_mb DOUBLE, 198 -- Garbage collection id. 199 gc_id LONG, 200 -- Garbage collection timestamp. 201 gc_ts TIMESTAMP, 202 -- Garbage collection wall duration. 203 gc_dur DURATION, 204 -- Garbage collection duration spent executing on CPU. 205 gc_running_dur DURATION, 206 -- Garbage collection duration spent waiting for CPU. 207 gc_runnable_dur DURATION, 208 -- Garbage collection duration spent waiting in the Linux kernel on IO. 209 gc_unint_io_dur DURATION, 210 -- Garbage collection duration spent waiting in the Linux kernel without IO. 211 gc_unint_non_io_dur DURATION, 212 -- Garbage collection duration spent waiting in interruptible sleep. 213 gc_int_dur LONG 214) AS 215WITH 216 agg_events AS ( 217 SELECT 218 tid, 219 pid, 220 utid, 221 upid, 222 thread_name, 223 process_name, 224 gc_type, 225 is_mark_compact, 226 gc_id, 227 gc_ts, 228 gc_dur, 229 sum(dur) AS dur, 230 max_heap_mb - min_heap_mb AS reclaimed_mb, 231 min_heap_mb, 232 max_heap_mb, 233 state, 234 io_wait 235 FROM _gc_slice_heap_thread_state_sp 236 GROUP BY 237 gc_id, 238 state, 239 io_wait 240 ) 241SELECT 242 tid, 243 pid, 244 utid, 245 upid, 246 thread_name, 247 process_name, 248 gc_type, 249 is_mark_compact, 250 reclaimed_mb, 251 min_heap_mb, 252 max_heap_mb, 253 gc_id, 254 gc_ts, 255 gc_dur, 256 sum(iif(state = 'Running', dur, 0)) AS gc_running_dur, 257 sum(iif(state = 'R' OR state = 'R+', dur, 0)) AS gc_runnable_dur, 258 sum(iif(state = 'D' AND io_wait = 1, dur, 0)) AS gc_unint_io_dur, 259 sum(iif(state = 'D' AND io_wait != 1, dur, 0)) AS gc_unint_non_io_dur, 260 sum(iif(state = 'S', dur, 0)) AS gc_int_dur 261FROM agg_events 262GROUP BY 263 gc_id; 264 265-- A window of the trace to use for GC stats. 266-- We can't reliably use trace_dur(), because often it spans far outside the 267-- range of relevant data. Instead pick the window based on when we have 268-- 'Heap size (KB)' data available. 269CREATE PERFETTO TABLE _gc_stats_window AS 270SELECT 271 min(ts) AS gc_stats_window_start, 272 max(ts) AS gc_stats_window_end, 273 max(ts) - min(ts) AS gc_stats_window_dur 274FROM counter AS c 275LEFT JOIN process_counter_track AS t 276 ON c.track_id = t.id 277WHERE 278 t.name = 'Heap size (KB)'; 279 280-- Count heap allocations by summing positive changes to the 'Heap size (KB)' 281-- counter. 282CREATE PERFETTO TABLE _gc_heap_allocated AS 283SELECT 284 upid, 285 ts, 286 ts - lag(ts) OVER (PARTITION BY upid ORDER BY ts) AS dur, 287 value, 288 CASE 289 WHEN lag(c.value) OVER (PARTITION BY upid ORDER BY ts) < c.value 290 THEN c.value - lag(c.value) OVER (PARTITION BY upid ORDER BY ts) 291 ELSE 0 292 END AS allocated 293FROM counter AS c 294LEFT JOIN process_counter_track AS t 295 ON c.track_id = t.id 296WHERE 297 t.name = 'Heap size (KB)'; 298 299-- Intersection of startup events and gcs, for understanding what GCs are 300-- happining during app startup. 301CREATE PERFETTO TABLE _gc_during_android_startup AS 302WITH 303 startups_for_intersect AS ( 304 SELECT 305 ts, 306 dur, 307 startup_id AS id 308 FROM android_startups 309 -- b/384732321 310 WHERE 311 dur > 0 312 ), 313 gcs_for_intersect AS ( 314 SELECT 315 gc_ts AS ts, 316 gc_dur AS dur, 317 gc_id AS id 318 FROM android_garbage_collection_events 319 ) 320SELECT 321 ts, 322 dur, 323 id_0 AS startup_id, 324 id_1 AS gc_id 325FROM _interval_intersect!((startups_for_intersect, gcs_for_intersect), ()); 326 327-- Estimate heap utilization across the trace. 328-- We weight the utilization by gc_period, which is meant to represent the time 329-- since the previous GC. We approximate gc_period as time to the start of the 330-- process or metrics utilization in case there is no previous GC for the 331-- process. 332CREATE PERFETTO TABLE _gc_heap_utilization AS 333WITH 334 -- The first GC for a process doesn't have a previous GC to calculate 335 -- gc_period from. Create pretend GCs at the start of each process to use 336 -- for computing gc_period for all the real GCs. 337 before_first_gcs AS ( 338 SELECT 339 upid, 340 CASE 341 WHEN start_ts IS NULL 342 THEN gc_stats_window_start 343 ELSE max(start_ts, gc_stats_window_start) 344 END AS gc_ts, 345 0 AS gc_dur, 346 0 AS min_heap_mb, 347 0 AS max_heap_mb 348 FROM process, _gc_stats_window 349 ), 350 combined_gcs AS ( 351 SELECT 352 upid, 353 gc_ts, 354 gc_dur, 355 min_heap_mb, 356 max_heap_mb 357 FROM android_garbage_collection_events 358 UNION 359 SELECT 360 * 361 FROM before_first_gcs 362 ), 363 gc_periods AS ( 364 SELECT 365 upid, 366 gc_ts + gc_dur - lag(gc_ts + gc_dur) OVER (PARTITION BY upid ORDER BY gc_ts) AS gc_period, 367 min_heap_mb, 368 max_heap_mb 369 FROM combined_gcs 370 ) 371SELECT 372 upid, 373 sum(gc_period * min_heap_mb) / 1e9 AS heap_live_mbs, 374 sum(gc_period * max_heap_mb) / 1e9 AS heap_total_mbs 375FROM gc_periods 376WHERE 377 min_heap_mb IS NOT NULL 378GROUP BY 379 upid; 380 381-- Summary stats about how garbage collection is behaving for a process, 382-- including causes, costs and other information relevant for tuning the 383-- garbage collector. 384CREATE PERFETTO TABLE _android_garbage_collection_process_stats ( 385 -- Upid of process the stats are for. 386 upid JOINID(process.id), 387 -- The start of the window of time that the stats cover in the trace. 388 ts TIMESTAMP, 389 -- The duration of the window of time that the stats cover in the trace. 390 dur DURATION, 391 -- Megabyte-seconds of heap size of the process, used in the calculation 392 -- of heap_size_mb. 393 heap_size_mbs DOUBLE, 394 -- Average heap size of the process, in MB. 395 heap_size_mb DOUBLE, 396 -- Total number of bytes allocated by the process in the window if interest. 397 heap_allocated_mb DOUBLE, 398 -- Rate of heap allocations in MB per second. 399 heap_allocation_rate DOUBLE, 400 -- Megabyte-seconds of live heap for processes that had GC events. 401 heap_live_mbs DOUBLE, 402 -- Megabyte-seconds of total heap for processes that had GC events. 403 heap_total_mbs DOUBLE, 404 -- Average heap utilization for the process. 405 heap_utilization DOUBLE, 406 -- CPU time spent running GC. Used in the calculation of gc_running_rate. 407 gc_running_dur DURATION, 408 -- CPU time spent doing GC, as a fraction of the duration of the trace. 409 -- This gives a sense of the battery cost of GC. 410 gc_running_rate DOUBLE, 411 -- A measure of how efficient GC is with respect to cpu, independent of how 412 -- aggressively GC is tuned. Larger values indicate more efficient GC, so 413 -- larger is better. 414 gc_running_efficiency DOUBLE, 415 -- Time GC is running in the process during startup of some other app. Used 416 -- in the calculation of gc_during_android_startup_rate. 417 gc_during_android_startup_dur DURATION, 418 -- Total startup time in the trace, used to normalize 419 -- the gc_during_android_startup_rate. 420 total_android_startup_dur DURATION, 421 -- Time GC in this process is running during app startup, as a fraction of 422 -- startup time in the trace. This gives a sense of how much potential 423 -- interference there is between GC and application startup. 424 gc_during_android_startup_rate DOUBLE, 425 -- A measure of how efficient GC is with regards to gc during application 426 -- startup, independent of how aggressively GC is tuned. Larger values 427 -- indicate more efficient GC, so larger is better. 428 gc_during_android_startup_efficiency DOUBLE 429) AS 430WITH 431 gc_running_stats AS ( 432 SELECT 433 upid, 434 sum(gc_running_dur) AS gc_running_dur 435 FROM android_garbage_collection_events 436 GROUP BY 437 upid 438 ), 439 heap_size_stats AS ( 440 SELECT 441 upid, 442 sum(allocated) / 1e3 AS heap_allocated_mb, 443 sum(value * dur) / ( 444 1e3 * 1e9 445 ) AS heap_size_mbs 446 FROM _gc_heap_allocated 447 GROUP BY 448 upid 449 ), 450 gc_startup_stats AS ( 451 SELECT 452 upid, 453 sum(dur) AS gc_during_android_startup_dur 454 FROM _gc_during_android_startup 455 LEFT JOIN android_garbage_collection_events 456 USING (gc_id) 457 GROUP BY 458 upid 459 ), 460 startup_stats AS ( 461 SELECT 462 sum(dur) AS total_android_startup_dur 463 FROM android_startups 464 ), 465 pre_normalized_stats AS ( 466 SELECT 467 * 468 FROM heap_size_stats 469 LEFT JOIN gc_running_stats 470 USING (upid) 471 LEFT JOIN gc_startup_stats 472 USING (upid) 473 LEFT JOIN _gc_heap_utilization 474 USING (upid), _gc_stats_window, startup_stats 475 ), 476 normalized_stats AS ( 477 SELECT 478 upid, 479 gc_running_dur * 1.0 / gc_stats_window_dur AS gc_running_rate, 480 gc_during_android_startup_dur * 1.0 / total_android_startup_dur AS gc_during_android_startup_rate, 481 heap_allocated_mb * 1e9 / gc_stats_window_dur AS heap_allocation_rate, 482 heap_size_mbs * 1e9 / gc_stats_window_dur AS heap_size_mb, 483 heap_live_mbs / heap_total_mbs AS heap_utilization 484 FROM pre_normalized_stats 485 ) 486SELECT 487 upid, 488 gc_stats_window_start AS ts, 489 gc_stats_window_dur AS dur, 490 heap_size_mbs, 491 heap_size_mb, 492 heap_allocated_mb, 493 heap_allocation_rate, 494 heap_live_mbs, 495 heap_total_mbs, 496 heap_utilization, 497 gc_running_dur, 498 gc_running_rate, 499 heap_allocation_rate * heap_utilization / ( 500 gc_running_rate * ( 501 1 - heap_utilization 502 ) 503 ) AS gc_running_efficiency, 504 gc_during_android_startup_dur, 505 total_android_startup_dur, 506 gc_during_android_startup_rate, 507 heap_allocation_rate * heap_utilization / ( 508 gc_during_android_startup_rate * ( 509 1 - heap_utilization 510 ) 511 ) AS gc_during_android_startup_efficiency 512FROM pre_normalized_stats 513JOIN normalized_stats 514 USING (upid); 515 516-- Summary stats about how garbage collection is behaving across the device, 517-- including causes, costs and other information relevant for tuning the 518-- garbage collector. 519CREATE PERFETTO TABLE _android_garbage_collection_stats ( 520 -- The start of the window of time that the stats cover in the trace. 521 ts TIMESTAMP, 522 -- The duration of the window of time that the stats cover in the trace. 523 dur DURATION, 524 -- Megabyte-seconds of heap size across the device, used in the calculation 525 -- of heap_size_mb. 526 heap_size_mbs DOUBLE, 527 -- Combined size of heaps across processes on the device on average, in MB. 528 heap_size_mb DOUBLE, 529 -- Total number of bytes allocated over the course of the trace. 530 heap_allocated_mb DOUBLE, 531 -- Combined rate of heap allocations in MB per second. This gives a sense of 532 -- how much allocation activity is going on during the trace. 533 heap_allocation_rate DOUBLE, 534 -- Megabyte-seconds of live heap for processes that had GC events. 535 heap_live_mbs DOUBLE, 536 -- Megabyte-seconds of total heap for processes that had GC events. 537 heap_total_mbs DOUBLE, 538 -- Overall heap utilization. This gives a sense of how aggressive GC is 539 -- during this trace. 540 heap_utilization DOUBLE, 541 -- CPU time spent running GC. Used in the calculation of gc_running_rate. 542 gc_running_dur DURATION, 543 -- CPU time spent doing GC, as a fraction of the duration of the trace. 544 -- This gives a sense of the battery cost of GC. 545 gc_running_rate DOUBLE, 546 -- A measure of how efficient GC is with respect to cpu, independent of how 547 -- aggressively GC is tuned. Larger values indicate more efficient GC, so 548 -- larger is better. 549 gc_running_efficiency DOUBLE, 550 -- Time GC is running during app startup. Used in the calculation of 551 -- gc_during_android_startup_rate. 552 gc_during_android_startup_dur DURATION, 553 -- Total startup time in the trace, used to normalize 554 -- the gc_during_android_startup_rate. 555 total_android_startup_dur DURATION, 556 -- Time GC is running during app startup, as a fraction of startup time in 557 -- the trace. This gives a sense of how much potential interference there 558 -- is between GC and application startup. 559 gc_during_android_startup_rate DOUBLE, 560 -- A measure of how efficient GC is with regards to gc during application 561 -- startup, independent of how aggressively GC is tuned. Larger values 562 -- indicate more efficient GC, so larger is better. 563 gc_during_android_startup_efficiency DOUBLE 564) AS 565WITH 566 base_stats AS ( 567 SELECT 568 ts, 569 dur, 570 sum(heap_size_mbs) AS heap_size_mbs, 571 sum(heap_size_mb) AS heap_size_mb, 572 sum(heap_allocated_mb) AS heap_allocated_mb, 573 sum(heap_allocation_rate) AS heap_allocation_rate, 574 sum(heap_live_mbs) AS heap_live_mbs, 575 sum(heap_total_mbs) AS heap_total_mbs, 576 sum(heap_live_mbs) / sum(heap_total_mbs) AS heap_utilization, 577 sum(gc_running_dur) AS gc_running_dur, 578 sum(gc_running_rate) AS gc_running_rate, 579 sum(gc_during_android_startup_dur) AS gc_during_android_startup_dur, 580 total_android_startup_dur, 581 sum(gc_during_android_startup_rate) AS gc_during_android_startup_rate 582 FROM _android_garbage_collection_process_stats 583 ) 584SELECT 585 *, 586 heap_allocation_rate * heap_utilization / ( 587 gc_running_rate * ( 588 1 - heap_utilization 589 ) 590 ) AS gc_running_efficiency, 591 heap_allocation_rate * heap_utilization / ( 592 gc_during_android_startup_rate * ( 593 1 - heap_utilization 594 ) 595 ) AS gc_during_android_startup_efficiency 596FROM base_stats; 597