• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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