@@ -18,24 +18,46 @@ INCLUDE PERFETTO MODULE android.oom_adjuster;
1818
1919INCLUDE PERFETTO MODULE counters .intervals ;
2020
21- -- Create a table containing intervals of memory counters values, adjusted to process lifetime.
22- CREATE PERFETTO TABLE _memory_breakdown_mem_intervals_raw AS
21+ -- A table of process memory counter intervals, clipped to the process lifetime.
22+ -- Provides a comprehensive view of memory usage, including raw values and
23+ -- zygote-adjusted values.
24+ --
25+ -- NOTE: For 'mem.rss.anon', 'mem.swap', 'mem.rss.file', and 'mem.heap' tracks, we
26+ -- subtract the Zygote's average memory usage. This provides a better estimate
27+ -- of the child process's unique memory usage by accounting for the
28+ -- baseline memory inherited from the Zygote.
29+ --
30+ -- NOTE: Some tracks may have a spike greater than 100MiB. This can be legitimate or
31+ -- an accounting issue: see b/418231246 for more details.
32+ CREATE PERFETTO TABLE android_process_memory_intervals (
33+ -- The id of the memory counter value
34+ id JOINID(counter .id ),
35+ -- Timestamp of the memory counter change.
36+ ts TIMESTAMP ,
37+ -- How long this memory track had this value.
38+ dur DURATION,
39+ -- The name of the process whose memory is being measured.
40+ process_name STRING,
41+ -- The unique id of the process whose memory is being measured.
42+ upid JOINID(process .upid ),
43+ -- The id of the process whose memory is being measured.
44+ pid JOINID(process .pid ),
45+ -- The name of the memory counter track (e.g. 'mem.rss.anon').
46+ memory_track_name STRING,
47+ -- The id of the memory counter track.
48+ track_id JOINID(track .id ),
49+ -- The value of the memory counter in bytes.
50+ value LONG,
51+ -- The value of the memory counter in bytes, adjusted with the zygote's memory usage.
52+ zygote_adjusted_value LONG,
53+ -- Whether the track has a spike greater than 100MiB.
54+ track_has_spike_gt_100mib BOOL
55+ ) AS
2356WITH
24- sched_bounds AS (
57+ -- Step 1: Prepare memory counter data.
58+ mem_tracks AS (
2559 SELECT
26- min (ts) AS min_ts,
27- max (ts + dur) AS max_ts
28- FROM sched
29- ),
30- trace_limits AS (
31- SELECT
32- coalesce(sb .min_ts , trace_start()) AS start_ts,
33- coalesce(sb .max_ts , trace_end()) AS end_ts
34- FROM sched_bounds AS sb
35- ),
36- mem_process_counter_tracks AS (
37- SELECT
38- id,
60+ id AS track_id,
3961 iif(name = ' Heap size (KB)' , ' mem.heap' , name) AS name,
4062 upid
4163 FROM process_counter_track
@@ -45,77 +67,128 @@ WITH
4567 mem_counters AS (
4668 SELECT
4769 c .id ,
48- c .ts ,
4970 c .track_id ,
50- iif(name = ' mem.heap' , cast_int!(c .value ) * 1024 , cast_int!(c .value )) AS value
71+ c .ts ,
72+ iif(t .name = ' mem.heap' , cast_int!(c .value ) * 1024 , cast_int!(c .value )) AS value
5173 FROM counter AS c
52- JOIN mem_process_counter_tracks AS t
53- ON c .track_id = t .id
74+ JOIN mem_tracks AS t
75+ ON c .track_id = t .track_id
5476 ),
5577 mem_intervals AS (
5678 SELECT
5779 ts,
5880 dur,
81+ id AS counter_id,
5982 track_id,
6083 value,
6184 delta_value
6285 FROM counter_leading_intervals!(mem_counters)
6386 ),
64- -- We deny tracks that have large swings in value
65- -- This can happen because of rss_stat accounting issue: see b/418231246 for details.
66- denied_tracks AS (
87+ -- Step 2: Identify tracks with large spikes (spikes > 100MiB).
88+ spikes AS (
6789 SELECT DISTINCT
6890 track_id
6991 FROM mem_intervals
7092 WHERE
71- -- Filter out changes larger than 100 MiB
7293 abs(delta_value) > 104857600
7394 ),
74- -- Get all memory counter values for all processes, and clip them to process lifetime .
75- mem_intervals_with_process_lifetime AS (
95+ -- Step 3: Join memory intervals with process lifetime and clip them.
96+ mem_intervals_clipped AS (
7697 SELECT
77- i . ts ,
78- i .ts + i .dur AS raw_end_ts ,
98+ max (ts, coalesce( p . start_ts , ts)) AS ts,
99+ min ( i .ts + i .dur , coalesce( p . end_ts , i . ts + i . dur )) - max ( i . ts , coalesce( p . start_ts , i . ts )) AS dur ,
79100 p .upid ,
80- p .start_ts ,
81- p .end_ts ,
82- t .name AS track_name,
101+ t .name AS memory_track_name,
83102 i .track_id ,
84- i .value
103+ i .counter_id ,
104+ i .value ,
105+ NOT s .track_id IS NULL AS track_has_spike_gt_100mib
85106 FROM mem_intervals AS i
86- JOIN mem_process_counter_tracks AS t
87- ON i .track_id = t .id
107+ JOIN mem_tracks AS t
108+ ON i .track_id = t .track_id
88109 JOIN process AS p
89110 USING (upid)
111+ LEFT JOIN spikes AS s
112+ USING (track_id)
113+ WHERE
114+ (
115+ min (i .ts + i .dur , coalesce(p .end_ts , i .ts + i .dur )) - max (i .ts , coalesce(p .start_ts , i .ts ))
116+ ) > 0
117+ ),
118+ -- Step 4: Calculate zygote memory baseline.
119+ -- TODO: improve zygote process detection
120+ zygote_processes AS (
121+ SELECT
122+ upid
123+ FROM process
124+ WHERE
125+ name IN (' zygote' , ' zygote64' , ' webview_zygote' )
126+ ),
127+ zygote_tracks AS (
128+ SELECT
129+ t .name AS memory_track_name,
130+ t .track_id
131+ FROM mem_tracks AS t
132+ JOIN zygote_processes AS z
133+ USING (upid)
90134 WHERE
91- NOT i .track_id IN (
92- SELECT
93- track_id
94- FROM denied_tracks
95- )
96- AND i .ts BETWEEN (
97- SELECT
98- start_ts
99- FROM trace_limits
100- ) AND (
101- SELECT
102- end_ts
103- FROM trace_limits
104- )
135+ t .name IN (' mem.rss.anon' , ' mem.swap' , ' mem.rss.file' , ' mem.heap' )
136+ ),
137+ zygote_baseline AS (
138+ SELECT
139+ max (CASE WHEN memory_track_name = ' mem.rss.anon' THEN avg_val END) AS rss_anon_base,
140+ max (CASE WHEN memory_track_name = ' mem.swap' THEN avg_val END) AS swap_base,
141+ max (CASE WHEN memory_track_name = ' mem.rss.file' THEN avg_val END) AS rss_file_base,
142+ max (CASE WHEN memory_track_name = ' mem.heap' THEN avg_val END) AS heap_base
143+ FROM (
144+ SELECT
145+ z .memory_track_name ,
146+ avg (cast_int!(c .value )) AS avg_val
147+ FROM mem_counters AS c
148+ JOIN zygote_tracks AS z
149+ USING (track_id)
150+ GROUP BY
151+ z .memory_track_name
152+ )
153+ ),
154+ -- Step 5: Join clipped intervals with zygote baseline.
155+ mem_intervals_with_zygote_baseline AS (
156+ SELECT
157+ c.* ,
158+ CASE
159+ WHEN c .memory_track_name = ' mem.rss.anon'
160+ THEN zb .rss_anon_base
161+ WHEN c .memory_track_name = ' mem.swap'
162+ THEN zb .swap_base
163+ WHEN c .memory_track_name = ' mem.rss.file'
164+ THEN zb .rss_file_base
165+ WHEN c .memory_track_name = ' mem.heap'
166+ THEN zb .heap_base
167+ ELSE 0
168+ END AS zygote_baseline_value
169+ FROM mem_intervals_clipped AS c
170+ CROSS JOIN zygote_baseline AS zb
105171 )
172+ -- Final Step: Compute the zygote-adjusted memory value.
106173SELECT
107- max (ts, coalesce(start_ts, ts)) AS ts,
108- min (raw_end_ts, coalesce(end_ts, raw_end_ts)) - max (ts, coalesce(start_ts, ts)) AS dur,
109- upid,
110- track_name,
111- track_id,
112- value
113- FROM mem_intervals_with_process_lifetime
114- -- Only keep rows where the clipping resulted in a positive duration.
115- WHERE
116- (
117- min (raw_end_ts, coalesce(end_ts, raw_end_ts)) - max (ts, coalesce(start_ts, ts))
118- ) > 0 ;
174+ d .counter_id AS id,
175+ d .ts ,
176+ d .dur ,
177+ p .name AS process_name,
178+ d .upid ,
179+ p .pid ,
180+ d .memory_track_name ,
181+ d .track_id ,
182+ d .value ,
183+ CASE
184+ WHEN NOT p .upid IS NULL AND NOT p .name IN (' zygote' , ' zygote64' , ' webview_zygote' )
185+ THEN max (0 , cast_int!(d .value ) - cast_int!(COALESCE(d .zygote_baseline_value , 0 )))
186+ ELSE cast_int!(d .value )
187+ END AS zygote_adjusted_value,
188+ d .track_has_spike_gt_100mib
189+ FROM mem_intervals_with_zygote_baseline AS d
190+ LEFT JOIN process AS p
191+ USING (upid);
119192
120193-- Create a table containing intervals of OOM adjustment scores.
121194-- This table will be used as the right side of a span join.
125198 SELECT
126199 track_id,
127200 upid
128- FROM _memory_breakdown_mem_intervals_raw
201+ FROM android_process_memory_intervals
129202 GROUP BY
130203 track_id,
131204 upid
@@ -142,82 +215,65 @@ WHERE
142215 o .dur > 0 ;
143216
144217CREATE VIRTUAL TABLE _memory_breakdown_mem_oom_span_join USING SPAN_LEFT_JOIN (
145- _memory_breakdown_mem_intervals_raw PARTITIONED track_id,
218+ android_process_memory_intervals PARTITIONED track_id,
146219 _memory_breakdown_oom_intervals_prepared PARTITIONED track_id
147220);
148221
149- -- Create a table containing memory counter intervals with OOM buckets.
150- CREATE PERFETTO TABLE _memory_breakdown_mem_with_buckets AS
151- WITH
152- -- Get the baseline values for RSS anon and swap from the zygote process.
153- zygote_upid AS (
154- SELECT
155- upid
156- FROM process
157- -- TODO: improve zygote process detection
158- WHERE
159- name IN (' zygote' , ' zygote64' , ' webview_zygote' )
160- ),
161- zygote_tracks AS (
162- SELECT
163- iif(t .name = ' Heap size (KB)' , ' mem.heap' , t .name ) AS track_name,
164- t .id AS track_id
165- FROM process_counter_track AS t
166- JOIN zygote_upid AS z
167- USING (upid)
168- WHERE
169- t .name IN (' mem.rss.anon' , ' mem.swap' , ' mem.rss.file' , ' Heap size (KB)' )
170- ),
171- zygote_baseline AS (
172- SELECT
173- max (CASE WHEN track_name = ' mem.rss.anon' THEN avg_val END) AS rss_anon_base,
174- max (CASE WHEN track_name = ' mem.swap' THEN avg_val END) AS swap_base,
175- max (CASE WHEN track_name = ' mem.rss.file' THEN avg_val END) AS rss_file_base,
176- max (CASE WHEN track_name = ' mem.heap' THEN avg_val END) AS heap_base
177- FROM (
178- SELECT
179- z .track_name ,
180- avg (iif(z .track_name = ' mem.heap' , cast_int!(c .value ) * 1024 , cast_int!(c .value ))) AS avg_val
181- FROM counter AS c
182- JOIN zygote_tracks AS z
183- USING (track_id)
184- GROUP BY
185- z .track_name
186- )
187- ),
188- mem_with_zygote_baseline AS (
189- SELECT
190- s.* ,
191- CASE
192- WHEN track_name = ' mem.rss.anon'
193- THEN b .rss_anon_base
194- WHEN track_name = ' mem.swap'
195- THEN b .swap_base
196- WHEN track_name = ' mem.rss.file'
197- THEN b .rss_file_base
198- WHEN track_name = ' mem.heap'
199- THEN b .heap_base
200- ELSE 0
201- END AS zygote_baseline_value
202- FROM _memory_breakdown_mem_oom_span_join AS s
203- CROSS JOIN zygote_baseline AS b
204- )
222+ -- Correlates memory counters with OOM adjustment scores.
223+ --
224+ -- This table joins memory counters with OOM adjustment scores, providing
225+ -- insights into memory usage under system memory pressure.
226+ --
227+ -- NOTE: For 'mem.rss.anon', 'mem.swap', 'mem.rss.file', and 'mem.heap' tracks, we
228+ -- subtract the Zygote's average memory usage. This provides a better estimate
229+ -- of the child process's unique memory usage by accounting for the
230+ -- baseline memory inherited from the Zygote.
231+ --
232+ -- NOTE: Some tracks may have a spike greater than 100MiB. This can be legitimate or
233+ -- an accounting issue: see b/418231246 for more details.
234+ CREATE PERFETTO TABLE android_process_memory_intervals_by_oom_bucket (
235+ -- Id.
236+ id LONG,
237+ -- The start timestamp of the interval.
238+ ts TIMESTAMP ,
239+ -- How long this memory track had this value.
240+ dur DURATION,
241+ -- The name of the process whose memory is being measured.
242+ process_name STRING,
243+ -- The unique id of the process whose memory is being measured.
244+ upid JOINID(process .upid ),
245+ -- The id of the process whose memory is being measured.
246+ pid JOINID(process .pid ),
247+ -- The OutOfMemory (OOM) adjustment score bucket (e.g. 'cached', 'background'). Defaults to 'unknown'
248+ -- if no OOM score is available for the interval.
249+ bucket STRING,
250+ -- The name of the memory counter track (e.g. 'mem.rss.anon').
251+ memory_track_name STRING,
252+ -- The id of the memory counter track.
253+ track_id JOINID(track .id ),
254+ -- The id of the memory counter value
255+ counter_id JOINID(counter .id ),
256+ -- The value of the memory counter in bytes.
257+ value LONG,
258+ -- The value of the memory counter in bytes, adjusted with the zygote's memory usage.
259+ zygote_adjusted_value LONG,
260+ -- Whether the track has a spike greater than 100MiB.
261+ track_has_spike_gt_100mib BOOL
262+ ) AS
205263SELECT
206264 row_number() OVER () AS id,
207- ts,
208- dur,
209- track_name,
210- p .name AS process_name,
211- upid,
212- pid,
213- coalesce(bucket, ' unknown' ) AS bucket,
214- CASE
215- WHEN NOT p .upid IS NULL AND NOT p .name IN (' zygote' , ' zygote64' , ' webview_zygote' )
216- THEN max (0 , cast_int!(value) - cast_int!(IFNULL(zygote_baseline_value, 0 )))
217- ELSE cast_int!(value)
218- END AS zygote_adjusted_value
219- FROM mem_with_zygote_baseline
220- LEFT JOIN process AS p
221- USING (upid)
265+ m .ts ,
266+ m .dur ,
267+ m .process_name ,
268+ m .upid ,
269+ m .pid ,
270+ coalesce(m .bucket , ' unknown' ) AS bucket,
271+ m .memory_track_name ,
272+ m .track_id ,
273+ m .id AS counter_id,
274+ m .value ,
275+ m .zygote_adjusted_value ,
276+ m .track_has_spike_gt_100mib
277+ FROM _memory_breakdown_mem_oom_span_join AS m
222278WHERE
223279 dur > 0 ;
0 commit comments