blob: ce2aeb4324d61383ec728641cec07115e44b5e70 [file] [log] [blame]
--
-- Copyright 2019 The Android Open Source Project
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- https://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
CREATE VIEW memory_delta AS
SELECT upid, SUM(size) AS delta
FROM heap_profile_allocation
GROUP BY 1;
CREATE VIEW memory_total AS
SELECT upid, SUM(size) AS total
FROM heap_profile_allocation
WHERE size > 0
GROUP BY 1;
-- Join frames with symbols and mappings to get a textual representation.
CREATE TABLE symbolized_frame AS
SELECT
frame_id,
symbol_name,
mapping_name,
HASH(symbol_name, mapping_name) frame_hash,
HeapProfileCallsites_Frame(
'name', symbol_name,
'mapping_name', mapping_name
) AS frame_proto
FROM (
SELECT
spf.id AS frame_id,
IFNULL(
(SELECT name FROM stack_profile_symbol symbol
WHERE symbol.symbol_set_id = spf.symbol_set_id
LIMIT 1),
spf.name
) AS symbol_name,
spm.name AS mapping_name
FROM stack_profile_frame spf
JOIN stack_profile_mapping spm
ON spf.mapping = spm.id
);
-- Required to join with callsites
CREATE UNIQUE INDEX symbolized_frame_idx ON symbolized_frame(frame_id);
-- View that joins callsites with frames. Allocation-agnostic, only used to
-- generated the hashed callsites.
CREATE TABLE callsites AS
SELECT cs.id, cs.parent_id, cs.depth, sf.frame_hash
FROM stack_profile_callsite cs
JOIN symbolized_frame sf USING(frame_id);
DROP INDEX symbolized_frame_idx;
-- heapprofd-based callsite ids are based on frame addresses, whereas we want
-- to group by symbol names.
-- Create a unique ID for each subtree by traversing from the root.
-- 1 self_hash can correspond to N callsite_ids (which can then be used to join
-- with allocs).
CREATE TABLE hashed_callsites AS
WITH RECURSIVE callsite_hasher(id, self_hash, parent_hash, frame_hash) AS (
SELECT
cs.id,
cs.frame_hash,
-1,
cs.frame_hash
FROM callsites cs
WHERE cs.depth = 0
UNION ALL
SELECT
child.id,
HASH(child.frame_hash, parent.self_hash),
parent.self_hash,
child.frame_hash
FROM callsite_hasher parent
JOIN callsites child
ON parent.id = child.parent_id
)
SELECT
self_hash,
parent_hash,
frame_hash,
id callsite_id
FROM callsite_hasher;
DROP TABLE callsites;
CREATE VIEW hashed_callsite_tree AS
SELECT DISTINCT self_hash, parent_hash, frame_hash
FROM hashed_callsites;
-- Required to join with allocs
CREATE INDEX hashed_callsites_id_idx ON hashed_callsites(callsite_id);
-- Computes the allocations for each hash-based callsite.
CREATE TABLE self_allocs AS
SELECT
hc.self_hash,
alloc.upid,
SUM(alloc.count) AS delta_count,
SUM(CASE WHEN alloc.count > 0 THEN alloc.count ELSE 0 END) AS total_count,
SUM(alloc.size) AS delta_bytes,
SUM(CASE WHEN alloc.size > 0 THEN alloc.size ELSE 0 END) AS total_bytes
FROM hashed_callsites hc
JOIN heap_profile_allocation alloc USING (callsite_id)
GROUP BY 1, 2;
DROP INDEX hashed_callsites_id_idx;
-- For each allocation (each self_alloc), emit a row for each ancestor and
-- aggregate them by self_hash.
CREATE TABLE child_allocs AS
WITH RECURSIVE parent_traversal(
self_hash, parent_hash, upid,
delta_count, total_count, delta_bytes, total_bytes) AS (
SELECT
sa.self_hash,
hc.parent_hash,
sa.upid,
sa.delta_count,
sa.total_count,
sa.delta_bytes,
sa.total_bytes
FROM self_allocs sa
JOIN hashed_callsite_tree hc ON sa.self_hash = hc.self_hash
UNION ALL
SELECT
parent.self_hash,
parent.parent_hash,
child.upid,
child.delta_count,
child.total_count,
child.delta_bytes,
child.total_bytes
FROM parent_traversal child
JOIN hashed_callsite_tree parent
ON child.parent_hash = parent.self_hash
)
SELECT
self_hash,
upid,
SUM(delta_count) AS delta_count,
SUM(total_count) AS total_count,
SUM(delta_bytes) AS delta_bytes,
SUM(total_bytes) AS total_bytes
FROM parent_traversal
GROUP BY 1, 2;
CREATE VIEW self_allocs_proto AS
SELECT
self_hash,
upid,
HeapProfileCallsites_Counters(
'delta_count', delta_count, 'total_count', total_count,
'delta_bytes', delta_bytes, 'total_bytes', total_bytes
) AS allocs_proto
FROM self_allocs;
CREATE VIEW child_allocs_proto AS
SELECT
self_hash,
upid,
HeapProfileCallsites_Counters(
'delta_count', delta_count, 'total_count', total_count,
'delta_bytes', delta_bytes, 'total_bytes', total_bytes
) AS allocs_proto
FROM child_allocs;
-- Required to map back to the symbol.
CREATE INDEX symbolized_frame_hash_idx ON symbolized_frame(frame_hash);
CREATE TABLE process_callsite AS
SELECT
ca.upid,
ca.self_hash,
tree.parent_hash,
frame.frame_proto,
sa.allocs_proto AS self_allocs_proto,
ca.allocs_proto AS child_allocs_proto
FROM hashed_callsite_tree tree
JOIN (SELECT DISTINCT frame_hash, frame_proto FROM symbolized_frame) frame
USING (frame_hash)
JOIN child_allocs_proto ca
USING (self_hash)
LEFT JOIN self_allocs_proto sa
USING (self_hash, upid)
ORDER BY 1, 2;
DROP INDEX symbolized_frame_hash_idx;
CREATE VIEW process_callsite_proto AS
SELECT
upid,
RepeatedField(HeapProfileCallsites_Callsite(
'hash', self_hash,
'parent_hash', parent_hash,
'frame', frame_proto,
'self_allocs', self_allocs_proto,
'child_allocs', child_allocs_proto
)) AS repeated_callsite_proto
FROM process_callsite
GROUP BY 1;
CREATE VIEW instance_stats_view AS
SELECT HeapProfileCallsites_InstanceStats(
'pid', process.pid,
'process_name', process.name,
'callsites', repeated_callsite_proto,
'profile_delta_bytes', memory_delta.delta,
'profile_total_bytes', memory_total.total
) AS instance_stats_proto
FROM process_callsite_proto
JOIN memory_total USING (upid)
JOIN memory_delta USING (upid)
JOIN process USING (upid);
CREATE VIEW heap_profile_callsites_output AS
SELECT HeapProfileCallsites(
'instance_stats',
(SELECT RepeatedField(instance_stats_proto) FROM instance_stats_view)
);