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 16INCLUDE PERFETTO MODULE graphs.dominator_tree; 17 18-- Excluding following types from the graph as they share objects' ownership 19-- with their real (more interesting) owners and will mask their idom to be the 20-- "super root". 21CREATE PERFETTO TABLE _ref_type_ids AS 22SELECT id AS type_id FROM heap_graph_class 23WHERE kind IN ( 24 'KIND_FINALIZER_REFERENCE', 25 'KIND_PHANTOM_REFERENCE', 26 'KIND_SOFT_REFERENCE', 27 'KIND_WEAK_REFERENCE'); 28 29CREATE PERFETTO TABLE _excluded_refs AS 30SELECT ref.id 31 FROM _ref_type_ids 32 JOIN heap_graph_object robj USING (type_id) 33 JOIN heap_graph_reference ref USING (reference_set_id) 34WHERE ref.field_name = 'java.lang.ref.Reference.referent' 35ORDER BY ref.id; 36 37-- The assigned id of the "super root". 38-- Since a Java heap graph is a "forest" structure, we need to add a imaginary 39-- "super root" node which connects all the roots of the forest into a single 40-- connected component, so that the dominator tree algorithm can be performed. 41CREATE PERFETTO FUNCTION memory_heap_graph_super_root_fn() 42-- The assigned id of the "super root". 43RETURNS INT AS 44SELECT max(id) + 1 FROM heap_graph_object; 45 46CREATE PERFETTO VIEW _dominator_compatible_heap_graph AS 47SELECT 48 ref.owner_id AS source_node_id, 49 ref.owned_id AS dest_node_id 50FROM heap_graph_reference ref 51JOIN heap_graph_object source_node ON ref.owner_id = source_node.id 52WHERE source_node.reachable 53 AND ref.id NOT IN _excluded_refs 54 AND ref.owned_id IS NOT NULL 55UNION ALL 56SELECT 57 (SELECT memory_heap_graph_super_root_fn()) as source_node_id, 58 id AS dest_node_id 59FROM heap_graph_object 60WHERE root_type IS NOT NULL; 61 62CREATE PERFETTO TABLE _heap_graph_dominator_tree AS 63SELECT 64 node_id AS id, 65 dominator_node_id AS idom_id 66FROM graph_dominator_tree!( 67 _dominator_compatible_heap_graph, 68 (SELECT memory_heap_graph_super_root_fn()) 69) 70-- Excluding the imaginary root. 71WHERE dominator_node_id IS NOT NULL 72-- Ordering by idom_id so queries below are faster when joining on idom_id. 73-- TODO(lalitm): support create index for Perfetto tables. 74ORDER BY idom_id; 75 76CREATE PERFETTO TABLE _heap_graph_dominator_tree_depth AS 77WITH RECURSIVE _tree_visitor(id, depth) AS ( 78 -- Let the super root have depth 0. 79 SELECT id, 1 AS depth 80 FROM _heap_graph_dominator_tree 81 WHERE idom_id IN (SELECT memory_heap_graph_super_root_fn()) 82 UNION ALL 83 SELECT child.id, parent.depth + 1 84 FROM _heap_graph_dominator_tree child 85 JOIN _tree_visitor parent ON child.idom_id = parent.id 86) 87SELECT * FROM _tree_visitor 88ORDER BY id; 89 90-- A performance note: we need 3 memoize functions because EXPERIMENTAL_MEMOIZE 91-- limits the function to return only 1 int. 92-- This means the exact same "memoized dfs pass" on the tree is done 3 times, so 93-- it takes 3x the time taken by only doing 1 pass. Doing only 1 pass would be 94-- possible if EXPERIMENTAL_MEMOIZE could return more than 1 int. 95 96CREATE PERFETTO FUNCTION _subtree_obj_count(id INT) 97RETURNS INT AS 98SELECT 1 + IFNULL(( 99 SELECT 100 SUM(_subtree_obj_count(child.id)) 101 FROM _heap_graph_dominator_tree child 102 WHERE child.idom_id = $id 103), 0); 104SELECT EXPERIMENTAL_MEMOIZE('_subtree_obj_count'); 105 106CREATE PERFETTO FUNCTION _subtree_size_bytes(id INT) 107RETURNS INT AS 108SELECT ( 109 SELECT self_size 110 FROM heap_graph_object 111 WHERE heap_graph_object.id = $id 112) + 113IFNULL(( 114 SELECT 115 SUM(_subtree_size_bytes(child.id)) 116 FROM _heap_graph_dominator_tree child 117 WHERE child.idom_id = $id 118), 0); 119SELECT EXPERIMENTAL_MEMOIZE('_subtree_size_bytes'); 120 121CREATE PERFETTO FUNCTION _subtree_native_size_bytes(id INT) 122RETURNS INT AS 123SELECT ( 124 SELECT native_size 125 FROM heap_graph_object 126 WHERE heap_graph_object.id = $id 127) + 128IFNULL(( 129 SELECT 130 SUM(_subtree_native_size_bytes(child.id)) 131 FROM _heap_graph_dominator_tree child 132 WHERE child.idom_id = $id 133), 0); 134SELECT EXPERIMENTAL_MEMOIZE('_subtree_native_size_bytes'); 135 136-- All reachable heap graph objects, their immediate dominators and summary of 137-- their dominated sets. 138-- The heap graph dominator tree is calculated by stdlib graphs.dominator_tree. 139-- Each reachable object is a node in the dominator tree, their immediate 140-- dominator is their parent node in the tree, and their dominated set is all 141-- their descendants in the tree. All size information come from the 142-- heap_graph_object prelude table. 143CREATE PERFETTO TABLE memory_heap_graph_dominator_tree ( 144 -- Heap graph object id. 145 id INT, 146 -- Immediate dominator object id of the object. 147 idom_id INT, 148 -- Count of all objects dominated by this object, self inclusive. 149 dominated_obj_count INT, 150 -- Total self_size of all objects dominated by this object, self inclusive. 151 dominated_size_bytes INT, 152 -- Total native_size of all objects dominated by this object, self inclusive. 153 dominated_native_size_bytes INT, 154 -- Depth of the object in the dominator tree. Depth of root objects are 1. 155 depth INT 156) AS 157SELECT 158 t.id, 159 t.idom_id, 160 _subtree_obj_count(t.id) AS dominated_obj_count, 161 _subtree_size_bytes(t.id) AS dominated_size_bytes, 162 _subtree_native_size_bytes(t.id) AS dominated_native_size_bytes, 163 d.depth 164FROM _heap_graph_dominator_tree t 165JOIN _heap_graph_dominator_tree_depth d USING(id) 166ORDER BY id; 167