Merge "tp: add depth column and super root fn to stdlib memory.heap_graph_dominator_tree" into main
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql b/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql
index 51217b0..2326aab 100644
--- a/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql
@@ -17,7 +17,7 @@
-- Excluding following types from the graph as they share objects' ownership
-- with their real (more interesting) owners and will mask their idom to be the
--- imaginary "root".
+-- "super root".
CREATE PERFETTO TABLE _excluded_type_ids AS
WITH RECURSIVE class_visitor(type_id) AS (
SELECT id AS type_id
@@ -33,6 +33,15 @@
)
SELECT * FROM class_visitor;
+-- The assigned id of the "super root".
+-- Since a Java heap graph is a "forest" structure, we need to add a imaginary
+-- "super root" node which connects all the roots of the forest into a single
+-- connected component, so that the dominator tree algorithm can be performed.
+CREATE PERFETTO FUNCTION memory_heap_graph_super_root_fn()
+-- The assigned id of the "super root".
+RETURNS INT AS
+SELECT max(id) + 1 FROM heap_graph_object;
+
CREATE PERFETTO VIEW _dominator_compatible_heap_graph AS
SELECT
ref.owner_id AS source_node_id,
@@ -42,11 +51,8 @@
WHERE source_node.reachable AND source_node.type_id NOT IN _excluded_type_ids
AND ref.owned_id IS NOT NULL
UNION ALL
--- Since a Java heap graph is a "forest" structure, we need to add a imaginary
--- "root" node which connects all the roots of the forest into a single
--- connected component.
SELECT
- (SELECT max(id) + 1 FROM heap_graph_object) as source_node_id,
+ (SELECT memory_heap_graph_super_root_fn()) as source_node_id,
id AS dest_node_id
FROM heap_graph_object
WHERE root_type IS NOT NULL;
@@ -57,7 +63,7 @@
dominator_node_id AS idom_id
FROM graph_dominator_tree!(
_dominator_compatible_heap_graph,
- (SELECT max(id) + 1 FROM heap_graph_object)
+ (SELECT memory_heap_graph_super_root_fn())
)
-- Excluding the imaginary root.
WHERE dominator_node_id IS NOT NULL
@@ -65,6 +71,20 @@
-- TODO(lalitm): support create index for Perfetto tables.
ORDER BY idom_id;
+CREATE PERFETTO TABLE _heap_graph_dominator_tree_depth AS
+WITH RECURSIVE _tree_visitor(id, depth) AS (
+ -- Let the super root have depth 0.
+ SELECT id, 1 AS depth
+ FROM _heap_graph_dominator_tree
+ WHERE idom_id IN (SELECT memory_heap_graph_super_root_fn())
+ UNION ALL
+ SELECT child.id, parent.depth + 1
+ FROM _heap_graph_dominator_tree child
+ JOIN _tree_visitor parent ON child.idom_id = parent.id
+)
+SELECT * FROM _tree_visitor
+ORDER BY id;
+
-- A performance note: we need 3 memoize functions because EXPERIMENTAL_MEMOIZE
-- limits the function to return only 1 int.
-- This means the exact same "memoized dfs pass" on the tree is done 3 times, so
@@ -128,12 +148,16 @@
-- Total self_size of all objects dominated by this object, self inclusive.
dominated_size_bytes INT,
-- Total native_size of all objects dominated by this object, self inclusive.
- dominated_native_size_bytes INT
+ dominated_native_size_bytes INT,
+ -- Depth of the object in the dominator tree. Depth of root objects are 1.
+ depth INT
) AS
SELECT
- id,
- idom_id,
- _subtree_obj_count(id) AS dominated_obj_count,
- _subtree_size_bytes(id) AS dominated_size_bytes,
- _subtree_native_size_bytes(id) AS dominated_native_size_bytes
-FROM _heap_graph_dominator_tree;
+ t.id,
+ t.idom_id,
+ _subtree_obj_count(t.id) AS dominated_obj_count,
+ _subtree_size_bytes(t.id) AS dominated_size_bytes,
+ _subtree_native_size_bytes(t.id) AS dominated_native_size_bytes,
+ d.depth
+FROM _heap_graph_dominator_tree t
+JOIN _heap_graph_dominator_tree_depth d USING(id);
diff --git a/test/trace_processor/diff_tests/stdlib/memory/heap_graph_dominator_tree_tests.py b/test/trace_processor/diff_tests/stdlib/memory/heap_graph_dominator_tree_tests.py
index 0dfa07e..52bb465 100644
--- a/test/trace_processor/diff_tests/stdlib/memory/heap_graph_dominator_tree_tests.py
+++ b/test/trace_processor/diff_tests/stdlib/memory/heap_graph_dominator_tree_tests.py
@@ -32,6 +32,7 @@
node.idom_id,
node.dominated_obj_count,
node.dominated_size_bytes,
+ node.depth,
cls.name AS type_name
FROM memory_heap_graph_dominator_tree node
JOIN heap_graph_object obj USING(id)
@@ -40,29 +41,42 @@
""",
out=Csv("""
"id","idom_id","dominated_obj_count","dominated_size_bytes",\
-"type_name"
- 0,12,1,3,"A"
- 2,12,1,3,"B"
- 4,12,4,12,"C"
- 1,12,2,6,"D"
- 3,12,1,3,"E"
- 5,4,1,3,"F"
- 6,4,2,6,"G"
- 8,12,1,3,"H"
- 9,12,1,3,"I"
- 10,6,1,3,"J"
- 11,12,1,3,"K"
- 7,1,1,3,"L"
- 13,22,6,922,"M"
- 16,22,3,100,"N"
- 14,13,4,904,"O"
- 15,13,1,16,"P"
- 17,16,1,32,"Q"
- 12,25,13,39,"R"
- 22,25,10,1023,"S"
- 18,16,1,64,"T"
- 19,14,1,128,"U"
- 20,14,1,256,"V"
- 21,14,1,512,"W"
- 23,25,1,1024,"java.lang.ref.FinalizerReference"
+"depth","type_name"
+ 0,12,1,3,2,"A"
+ 2,12,1,3,2,"B"
+ 4,12,4,12,2,"C"
+ 1,12,2,6,2,"D"
+ 3,12,1,3,2,"E"
+ 5,4,1,3,3,"F"
+ 6,4,2,6,3,"G"
+ 8,12,1,3,2,"H"
+ 9,12,1,3,2,"I"
+ 10,6,1,3,4,"J"
+ 11,12,1,3,2,"K"
+ 7,1,1,3,3,"L"
+ 13,22,6,922,2,"M"
+ 16,22,3,100,2,"N"
+ 14,13,4,904,3,"O"
+ 15,13,1,16,3,"P"
+ 17,16,1,32,3,"Q"
+ 12,25,13,39,1,"R"
+ 22,25,10,1023,1,"S"
+ 18,16,1,64,3,"T"
+ 19,14,1,128,4,"U"
+ 20,14,1,256,4,"V"
+ 21,14,1,512,4,"W"
+ 23,25,1,1024,1,"java.lang.ref.FinalizerReference"
+ """))
+
+ def test_heap_graph_super_root_fn(self):
+ return DiffTestBlueprint(
+ trace=Path('heap_graph_for_dominator_tree.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE memory.heap_graph_dominator_tree;
+
+ SELECT memory_heap_graph_super_root_fn();
+ """,
+ out=Csv("""
+ "memory_heap_graph_super_root_fn()"
+ 25
"""))