1-- 2-- Copyright 2019 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 17SELECT RUN_METRIC('android/process_metadata.sql'); 18SELECT RUN_METRIC('android/process_mem.sql'); 19 20CREATE VIEW memory_delta AS 21SELECT upid, SUM(size) AS delta 22FROM heap_profile_allocation 23GROUP BY 1; 24 25CREATE VIEW memory_total AS 26SELECT upid, SUM(size) AS total 27FROM heap_profile_allocation 28WHERE size > 0 29GROUP BY 1; 30 31-- Join frames with symbols and mappings to get a textual representation. 32CREATE TABLE symbolized_frame AS 33SELECT 34 frame_id, 35 symbol_name, 36 mapping_name, 37 HASH(symbol_name, mapping_name) frame_hash, 38 HeapProfileCallsites_Frame( 39 'name', symbol_name, 40 'mapping_name', mapping_name 41 ) AS frame_proto 42FROM ( 43 SELECT 44 spf.id AS frame_id, 45 COALESCE( 46 (SELECT name FROM stack_profile_symbol symbol 47 WHERE symbol.symbol_set_id = spf.symbol_set_id 48 LIMIT 1), 49 spf.deobfuscated_name, 50 spf.name 51 ) AS symbol_name, 52 spm.name AS mapping_name 53 FROM stack_profile_frame spf 54 JOIN stack_profile_mapping spm 55 ON spf.mapping = spm.id 56); 57 58-- Required to join with callsites 59CREATE UNIQUE INDEX symbolized_frame_idx ON symbolized_frame(frame_id); 60 61-- View that joins callsites with frames. Allocation-agnostic, only used to 62-- generated the hashed callsites. 63CREATE TABLE callsites AS 64SELECT cs.id, cs.parent_id, cs.depth, sf.frame_hash 65FROM stack_profile_callsite cs 66JOIN symbolized_frame sf USING(frame_id); 67 68DROP INDEX symbolized_frame_idx; 69 70-- heapprofd-based callsite ids are based on frame addresses, whereas we want 71-- to group by symbol names. 72-- Create a unique ID for each subtree by traversing from the root. 73-- 1 self_hash can correspond to N callsite_ids (which can then be used to join 74-- with allocs). 75CREATE TABLE hashed_callsites AS 76WITH RECURSIVE callsite_hasher(id, self_hash, parent_hash, frame_hash) AS ( 77 SELECT 78 cs.id, 79 cs.frame_hash, 80 -1, 81 cs.frame_hash 82 FROM callsites cs 83 WHERE cs.depth = 0 84 UNION ALL 85 SELECT 86 child.id, 87 HASH(child.frame_hash, parent.self_hash), 88 parent.self_hash, 89 child.frame_hash 90 FROM callsite_hasher parent 91 JOIN callsites child 92 ON parent.id = child.parent_id 93) 94SELECT 95 self_hash, 96 parent_hash, 97 frame_hash, 98 id callsite_id 99FROM callsite_hasher; 100 101DROP TABLE callsites; 102 103CREATE VIEW hashed_callsite_tree AS 104SELECT DISTINCT self_hash, parent_hash, frame_hash 105FROM hashed_callsites; 106 107-- Required to join with allocs 108CREATE INDEX hashed_callsites_id_idx ON hashed_callsites(callsite_id); 109 110-- Computes the allocations for each hash-based callsite. 111CREATE TABLE self_allocs AS 112SELECT 113 hc.self_hash, 114 alloc.upid, 115 SUM(alloc.count) AS delta_count, 116 SUM(CASE WHEN alloc.count > 0 THEN alloc.count ELSE 0 END) AS total_count, 117 SUM(alloc.size) AS delta_bytes, 118 SUM(CASE WHEN alloc.size > 0 THEN alloc.size ELSE 0 END) AS total_bytes 119FROM hashed_callsites hc 120JOIN heap_profile_allocation alloc USING (callsite_id) 121GROUP BY 1, 2; 122 123DROP INDEX hashed_callsites_id_idx; 124 125-- For each allocation (each self_alloc), emit a row for each ancestor and 126-- aggregate them by self_hash. 127CREATE TABLE child_allocs AS 128WITH RECURSIVE parent_traversal( 129 self_hash, parent_hash, upid, 130 delta_count, total_count, delta_bytes, total_bytes) AS ( 131 SELECT 132 sa.self_hash, 133 hc.parent_hash, 134 sa.upid, 135 sa.delta_count, 136 sa.total_count, 137 sa.delta_bytes, 138 sa.total_bytes 139 FROM self_allocs sa 140 JOIN hashed_callsite_tree hc ON sa.self_hash = hc.self_hash 141 UNION ALL 142 SELECT 143 parent.self_hash, 144 parent.parent_hash, 145 child.upid, 146 child.delta_count, 147 child.total_count, 148 child.delta_bytes, 149 child.total_bytes 150 FROM parent_traversal child 151 JOIN hashed_callsite_tree parent 152 ON child.parent_hash = parent.self_hash 153) 154SELECT 155 self_hash, 156 upid, 157 SUM(delta_count) AS delta_count, 158 SUM(total_count) AS total_count, 159 SUM(delta_bytes) AS delta_bytes, 160 SUM(total_bytes) AS total_bytes 161FROM parent_traversal 162GROUP BY 1, 2; 163 164CREATE VIEW self_allocs_proto AS 165SELECT 166 self_hash, 167 upid, 168 HeapProfileCallsites_Counters( 169 'delta_count', delta_count, 'total_count', total_count, 170 'delta_bytes', delta_bytes, 'total_bytes', total_bytes 171 ) AS allocs_proto 172FROM self_allocs; 173 174CREATE VIEW child_allocs_proto AS 175SELECT 176 self_hash, 177 upid, 178 HeapProfileCallsites_Counters( 179 'delta_count', delta_count, 'total_count', total_count, 180 'delta_bytes', delta_bytes, 'total_bytes', total_bytes 181 ) AS allocs_proto 182FROM child_allocs; 183 184-- Required to map back to the symbol. 185CREATE INDEX symbolized_frame_hash_idx ON symbolized_frame(frame_hash); 186 187CREATE TABLE process_callsite AS 188SELECT 189 ca.upid, 190 ca.self_hash, 191 tree.parent_hash, 192 frame.frame_proto, 193 sa.allocs_proto AS self_allocs_proto, 194 ca.allocs_proto AS child_allocs_proto 195FROM hashed_callsite_tree tree 196JOIN (SELECT DISTINCT frame_hash, frame_proto FROM symbolized_frame) frame 197 USING (frame_hash) 198JOIN child_allocs_proto ca 199 USING (self_hash) 200LEFT JOIN self_allocs_proto sa 201 USING (self_hash, upid) 202ORDER BY 1, 2; 203 204DROP INDEX symbolized_frame_hash_idx; 205 206CREATE VIEW process_callsite_proto AS 207SELECT 208 upid, 209 RepeatedField(HeapProfileCallsites_Callsite( 210 'hash', self_hash, 211 'parent_hash', parent_hash, 212 'frame', frame_proto, 213 'self_allocs', self_allocs_proto, 214 'child_allocs', child_allocs_proto 215 )) AS repeated_callsite_proto 216FROM process_callsite 217GROUP BY 1; 218 219CREATE VIEW instance_stats_view AS 220SELECT HeapProfileCallsites_InstanceStats( 221 'pid', process.pid, 222 'process_name', process.name, 223 'process', process_metadata.metadata, 224 'callsites', repeated_callsite_proto, 225 'profile_delta_bytes', memory_delta.delta, 226 'profile_total_bytes', memory_total.total, 227 'max_anon_rss_and_swap_bytes', ( 228 SELECT CAST(MAX(anon_and_swap_val) AS INT) 229 FROM anon_and_swap_span s WHERE s.upid = process.upid 230 ) 231) AS instance_stats_proto 232FROM process_callsite_proto 233JOIN memory_total USING (upid) 234JOIN memory_delta USING (upid) 235JOIN process USING (upid) 236JOIN process_metadata USING (upid); 237 238CREATE VIEW heap_profile_callsites_output AS 239SELECT HeapProfileCallsites( 240 'instance_stats', 241 (SELECT RepeatedField(instance_stats_proto) FROM instance_stats_view) 242); 243