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.with_context; 18 19-- Collect all GC slices. There's typically one enclosing slice but sometimes the 20-- CompactionPhase is outside the nesting and we need to include that. 21CREATE PERFETTO VIEW _gc_slice 22AS 23WITH concurrent AS ( 24SELECT 25 id AS gc_id, 26 name AS gc_name, 27 LEAD(name) OVER (PARTITION BY track_id ORDER BY ts) AS compact_name, 28 LEAD(dur) OVER (PARTITION BY track_id ORDER BY ts) AS compact_dur, 29 ts AS gc_ts, 30 IIF(dur = -1, trace_end() - slice.ts, slice.dur) AS gc_dur, 31 ts, 32 dur, 33 tid, 34 utid, 35 pid, 36 upid, 37 thread_name, 38 process_name 39FROM thread_slice slice 40WHERE depth = 0 41) SELECT 42 gc_id, 43 gc_name, 44 ts AS gc_ts, 45 ts, 46 gc_dur + IIF( 47 compact_name = 'CompactionPhase' OR compact_name = 'Background concurrent copying GC', 48 compact_dur, 49 0) AS gc_dur, 50 gc_dur + IIF( 51 compact_name = 'CompactionPhase' OR compact_name = 'Background concurrent copying GC', 52 compact_dur, 53 0) AS dur, 54 utid, 55 tid, 56 upid, 57 pid, 58 thread_name, 59 process_name 60FROM concurrent WHERE gc_name GLOB '*concurrent*GC'; 61 62-- Extract the heap counter into <ts, dur, upid> 63CREATE PERFETTO VIEW _gc_heap_counter 64AS 65SELECT 66 c.ts, 67 IFNULL(lead(c.ts) OVER (PARTITION BY track_id ORDER BY c.ts), trace_end()) - ts 68 AS dur, 69 process.upid, 70 CAST(c.value AS INT) AS value 71FROM counter c 72JOIN process_counter_track t 73 ON c.track_id = t.id 74INNER JOIN process 75 USING (upid) 76WHERE 77 t.name = 'Heap size (KB)'; 78 79-- Find the last heap counter after the GC slice dur. This is the best effort to find the 80-- final heap size after GC. The algorithm is like so: 81-- 1. Merge end_ts of the GC events with the start_ts of the heap counters. 82-- 2. Find the heap counter value right after each GC event. 83CREATE PERFETTO VIEW _gc_slice_with_final_heap 84AS 85WITH 86 slice_and_heap AS ( 87 SELECT upid, gc_id, gc_ts + gc_dur AS ts, NULL AS value FROM _gc_slice 88 UNION ALL 89 SELECT upid, NULL AS gc_id, ts, value FROM _gc_heap_counter 90 ), 91 next_heap AS ( 92 SELECT *, lead(value) OVER (PARTITION BY upid ORDER BY ts) AS last_value FROM slice_and_heap 93 ), 94 slice_with_last_heap AS ( 95 SELECT * FROM next_heap WHERE gc_id IS NOT NULL 96 ) 97 SELECT _gc_slice.*, last_value FROM _gc_slice LEFT JOIN slice_with_last_heap USING (gc_id); 98 99-- Span join with all the other heap counters to find the overall min and max heap size. 100CREATE VIRTUAL TABLE _gc_slice_heap_sp 101USING 102 SPAN_JOIN(_gc_slice_with_final_heap PARTITIONED upid, _gc_heap_counter PARTITIONED upid); 103 104-- Aggregate the min and max heap across the GC event, taking into account the last heap size 105-- derived earlier. 106CREATE PERFETTO TABLE _gc_slice_heap 107AS 108SELECT 109 *, 110 CASE 111 WHEN gc_name GLOB '*young*' THEN 'young' 112 WHEN gc_name GLOB '*NativeAlloc*' THEN 'native_alloc' 113 WHEN gc_name GLOB '*Alloc*' THEN 'alloc' 114 WHEN gc_name GLOB '*CollectorTransition*' THEN 'collector_transition' 115 WHEN gc_name GLOB '*Explicit*' THEN 'explicit' 116 ELSE 'full' 117 END AS gc_type, 118 IIF(gc_name GLOB '*mark compact*', 1, 0) AS is_mark_compact, 119 MAX(MAX(value, last_value))/1e3 AS max_heap_mb, 120 MIN(MIN(value, last_value))/1e3 AS min_heap_mb 121FROM _gc_slice_heap_sp 122GROUP BY gc_id; 123 124-- Span join GC events with thread states to breakdown the time spent. 125CREATE VIRTUAL TABLE _gc_slice_heap_thread_state_sp 126USING 127 SPAN_JOIN(thread_state PARTITIONED utid, _gc_slice_heap PARTITIONED utid); 128 129-- All Garbage collection events with a breakdown of the time spent and heap reclaimed. 130CREATE PERFETTO TABLE android_garbage_collection_events ( 131 -- Tid of thread running garbage collection. 132 tid INT, 133 -- Pid of process running garbage collection. 134 pid INT, 135 -- Utid of thread running garbage collection. 136 utid INT, 137 -- Upid of process running garbage collection. 138 upid INT, 139 -- Name of thread running garbage collection. 140 thread_name INT, 141 -- Name of process running garbage collection. 142 process_name INT, 143 -- Type of garbage collection. 144 gc_type STRING, 145 -- Whether gargage collection is mark compact or copying. 146 is_mark_compact INT, 147 -- MB reclaimed after garbage collection. 148 reclaimed_mb INT, 149 -- Minimum heap size in MB during garbage collection. 150 min_heap_mb INT, 151 -- Maximum heap size in MB during garbage collection. 152 max_heap_mb INT, 153 -- Garbage collection id. 154 gc_id INT, 155 -- Garbage collection timestamp. 156 gc_ts INT, 157 -- Garbage collection wall duration. 158 gc_dur INT, 159 -- Garbage collection duration spent executing on CPU. 160 gc_running_dur INT, 161 -- Garbage collection duration spent waiting for CPU. 162 gc_runnable_dur INT, 163 -- Garbage collection duration spent waiting in the Linux kernel on IO. 164 gc_unint_io_dur INT, 165 -- Garbage collection duration spent waiting in the Linux kernel without IO. 166 gc_unint_non_io_dur INT, 167 -- Garbage collection duration spent waiting in interruptible sleep. 168 gc_int_dur INT 169 ) 170AS 171WITH 172 agg_events AS ( 173 SELECT 174 tid, 175 pid, 176 utid, 177 upid, 178 thread_name, 179 process_name, 180 gc_type, 181 is_mark_compact, 182 gc_id, 183 gc_ts, 184 gc_dur, 185 SUM(dur) AS dur, 186 max_heap_mb - min_heap_mb AS reclaimed_mb, 187 min_heap_mb, 188 max_heap_mb, 189 state, 190 io_wait 191 FROM _gc_slice_heap_thread_state_sp 192 GROUP BY gc_id, state, io_wait 193 ) 194SELECT 195 tid, 196 pid, 197 utid, 198 upid, 199 thread_name, 200 process_name, 201 gc_type, 202 is_mark_compact, 203 reclaimed_mb, 204 min_heap_mb, 205 max_heap_mb, 206 gc_id, 207 gc_ts, 208 gc_dur, 209 SUM(IIF(state = 'Running', dur, 0)) AS gc_running_dur, 210 SUM(IIF(state = 'R' OR state = 'R+', dur, 0)) AS gc_runnable_dur, 211 SUM(IIF(state = 'D' AND io_wait = 1, dur, 0)) AS gc_unint_io_dur, 212 SUM(IIF(state = 'D' AND io_wait != 1, dur, 0)) AS gc_unint_non_io_dur, 213 SUM(IIF(state = 'S', dur, 0)) AS gc_int_dur 214FROM agg_events 215GROUP BY gc_id; 216