Skip to content

Commit bb8483a

Browse files
authored
stdlib: memory_breakdown expose schema (#4982)
This change refactors memory_breakdown.sql: - moves the computation of zygote_adjusted_value before the left span join, and added it together with the raw counter value - marks tracks with spikes bigger than 100MiB (instead of removing) - removed the trace limits filter (will filter only for metrics) - added table schemas - renamed tables
1 parent a77ea94 commit bb8483a

File tree

2 files changed

+199
-143
lines changed

2 files changed

+199
-143
lines changed

src/trace_processor/perfetto_sql/stdlib/android/memory/memory_breakdown.sql

Lines changed: 189 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,46 @@ INCLUDE PERFETTO MODULE android.oom_adjuster;
1818

1919
INCLUDE 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
2356
WITH
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.
106173
SELECT
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.
@@ -125,7 +198,7 @@ WITH
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

144217
CREATE 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
205263
SELECT
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
222278
WHERE
223279
dur > 0;

0 commit comments

Comments
 (0)