Add java_heap_class_stats metric covering dominator stats
Bug: b/327171932
Change-Id: I01fbfc16389d0da9acb2ddfd6f4c92af719f5007
diff --git a/Android.bp b/Android.bp
index 94b84ff..5c153d6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5192,6 +5192,7 @@
"protos/perfetto/metrics/android/ion_metric.proto",
"protos/perfetto/metrics/android/irq_runtime_metric.proto",
"protos/perfetto/metrics/android/jank_cuj_metric.proto",
+ "protos/perfetto/metrics/android/java_heap_class_stats.proto",
"protos/perfetto/metrics/android/java_heap_histogram.proto",
"protos/perfetto/metrics/android/java_heap_stats.proto",
"protos/perfetto/metrics/android/lmk_metric.proto",
@@ -5282,6 +5283,7 @@
"protos/perfetto/metrics/android/ion_metric.proto",
"protos/perfetto/metrics/android/irq_runtime_metric.proto",
"protos/perfetto/metrics/android/jank_cuj_metric.proto",
+ "protos/perfetto/metrics/android/java_heap_class_stats.proto",
"protos/perfetto/metrics/android/java_heap_histogram.proto",
"protos/perfetto/metrics/android/java_heap_stats.proto",
"protos/perfetto/metrics/android/lmk_metric.proto",
@@ -5356,6 +5358,7 @@
"protos/perfetto/metrics/android/ion_metric.proto",
"protos/perfetto/metrics/android/irq_runtime_metric.proto",
"protos/perfetto/metrics/android/jank_cuj_metric.proto",
+ "protos/perfetto/metrics/android/java_heap_class_stats.proto",
"protos/perfetto/metrics/android/java_heap_histogram.proto",
"protos/perfetto/metrics/android/java_heap_stats.proto",
"protos/perfetto/metrics/android/lmk_metric.proto",
@@ -12079,6 +12082,7 @@
"src/trace_processor/metrics/sql/android/jank/relevant_slices.sql",
"src/trace_processor/metrics/sql/android/jank/relevant_threads.sql",
"src/trace_processor/metrics/sql/android/jank/slices.sql",
+ "src/trace_processor/metrics/sql/android/java_heap_class_stats.sql",
"src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
"src/trace_processor/metrics/sql/android/java_heap_stats.sql",
"src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
diff --git a/BUILD b/BUILD
index c6408f1..6795a44 100644
--- a/BUILD
+++ b/BUILD
@@ -2034,6 +2034,7 @@
"src/trace_processor/metrics/sql/android/jank/relevant_slices.sql",
"src/trace_processor/metrics/sql/android/jank/relevant_threads.sql",
"src/trace_processor/metrics/sql/android/jank/slices.sql",
+ "src/trace_processor/metrics/sql/android/java_heap_class_stats.sql",
"src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
"src/trace_processor/metrics/sql/android/java_heap_stats.sql",
"src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
@@ -4471,6 +4472,7 @@
"protos/perfetto/metrics/android/ion_metric.proto",
"protos/perfetto/metrics/android/irq_runtime_metric.proto",
"protos/perfetto/metrics/android/jank_cuj_metric.proto",
+ "protos/perfetto/metrics/android/java_heap_class_stats.proto",
"protos/perfetto/metrics/android/java_heap_histogram.proto",
"protos/perfetto/metrics/android/java_heap_stats.proto",
"protos/perfetto/metrics/android/lmk_metric.proto",
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 5430c22..2bb8c0e 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -52,6 +52,7 @@
"ion_metric.proto",
"irq_runtime_metric.proto",
"jank_cuj_metric.proto",
+ "java_heap_class_stats.proto",
"java_heap_histogram.proto",
"java_heap_stats.proto",
"lmk_metric.proto",
diff --git a/protos/perfetto/metrics/android/java_heap_class_stats.proto b/protos/perfetto/metrics/android/java_heap_class_stats.proto
new file mode 100644
index 0000000..ecde153
--- /dev/null
+++ b/protos/perfetto/metrics/android/java_heap_class_stats.proto
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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
+ *
+ * http://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.
+ */
+syntax = "proto2";
+package perfetto.protos;
+
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
+message JavaHeapClassStats {
+ // Next id: 11
+ message TypeCount {
+ optional string type_name = 1;
+ optional int64 obj_count = 2;
+ optional int64 size_bytes = 3;
+ optional int64 native_size_bytes = 4;
+ optional int64 reachable_obj_count = 5;
+ optional int64 reachable_size_bytes = 6;
+ optional int64 reachable_native_size_bytes = 7;
+ optional int64 dominated_obj_count = 8;
+ optional int64 dominated_size_bytes = 9;
+ optional int64 dominated_native_size_bytes = 10;
+ }
+
+ message Sample {
+ optional int64 ts = 1;
+ repeated TypeCount type_count = 2;
+ }
+
+ // Stats per process. One sample per dump (with continuous dump you can
+ // have more samples differentiated by ts).
+ message InstanceStats {
+ optional uint32 upid = 1;
+ optional perfetto.protos.AndroidProcessMetadata process = 2;
+ repeated Sample samples = 3;
+ }
+
+ repeated InstanceStats instance_stats = 1;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index c3025c3..e5a9553 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -48,6 +48,7 @@
import "protos/perfetto/metrics/android/irq_runtime_metric.proto";
import "protos/perfetto/metrics/android/jank_cuj_metric.proto";
import "protos/perfetto/metrics/android/java_heap_histogram.proto";
+import "protos/perfetto/metrics/android/java_heap_class_stats.proto";
import "protos/perfetto/metrics/android/java_heap_stats.proto";
import "protos/perfetto/metrics/android/lmk_metric.proto";
import "protos/perfetto/metrics/android/lmk_reason_metric.proto";
@@ -118,7 +119,7 @@
// Root message for all Perfetto-based metrics.
//
-// Next id: 66
+// Next id: 68
message TraceMetrics {
reserved 4, 10, 13, 14, 16, 19;
@@ -170,6 +171,9 @@
// If the trace contains a heap graph, output histogram.
optional JavaHeapHistogram java_heap_histogram = 21;
+ // If the trace contains a heap graph, output stats per heap class.
+ optional JavaHeapClassStats java_heap_class_stats = 67;
+
// Metrics used to find potential culprits of low-memory kills.
optional AndroidLmkReasonMetric android_lmk_reason = 18;
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 86f7d59..a4da778 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -1441,6 +1441,41 @@
// End of protos/perfetto/metrics/android/jank_cuj_metric.proto
+// Begin of protos/perfetto/metrics/android/java_heap_class_stats.proto
+
+message JavaHeapClassStats {
+ // Next id: 11
+ message TypeCount {
+ optional string type_name = 1;
+ optional int64 obj_count = 2;
+ optional int64 size_bytes = 3;
+ optional int64 native_size_bytes = 4;
+ optional int64 reachable_obj_count = 5;
+ optional int64 reachable_size_bytes = 6;
+ optional int64 reachable_native_size_bytes = 7;
+ optional int64 dominated_obj_count = 8;
+ optional int64 dominated_size_bytes = 9;
+ optional int64 dominated_native_size_bytes = 10;
+ }
+
+ message Sample {
+ optional int64 ts = 1;
+ repeated TypeCount type_count = 2;
+ }
+
+ // Stats per process. One sample per dump (with continuous dump you can
+ // have more samples differentiated by ts).
+ message InstanceStats {
+ optional uint32 upid = 1;
+ optional perfetto.protos.AndroidProcessMetadata process = 2;
+ repeated Sample samples = 3;
+ }
+
+ repeated InstanceStats instance_stats = 1;
+}
+
+// End of protos/perfetto/metrics/android/java_heap_class_stats.proto
+
// Begin of protos/perfetto/metrics/android/java_heap_histogram.proto
message JavaHeapHistogram {
@@ -2608,7 +2643,7 @@
// Root message for all Perfetto-based metrics.
//
-// Next id: 66
+// Next id: 68
message TraceMetrics {
reserved 4, 10, 13, 14, 16, 19;
@@ -2660,6 +2695,9 @@
// If the trace contains a heap graph, output histogram.
optional JavaHeapHistogram java_heap_histogram = 21;
+ // If the trace contains a heap graph, output stats per heap class.
+ optional JavaHeapClassStats java_heap_class_stats = 67;
+
// Metrics used to find potential culprits of low-memory kills.
optional AndroidLmkReasonMetric android_lmk_reason = 18;
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index c337e46..59b0f2a 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index 7de8cf6..9eb8b04 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -92,6 +92,7 @@
"jank/relevant_slices.sql",
"jank/relevant_threads.sql",
"jank/slices.sql",
+ "java_heap_class_stats.sql",
"java_heap_histogram.sql",
"java_heap_stats.sql",
"mem_stats_priority_breakdown.sql",
diff --git a/src/trace_processor/metrics/sql/android/java_heap_class_stats.sql b/src/trace_processor/metrics/sql/android/java_heap_class_stats.sql
new file mode 100644
index 0000000..4fb79c3
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/java_heap_class_stats.sql
@@ -0,0 +1,108 @@
+--
+-- Copyright 2024 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.
+--
+
+SELECT RUN_METRIC('android/process_metadata.sql');
+
+INCLUDE PERFETTO MODULE memory.heap_graph_dominator_tree;
+INCLUDE PERFETTO MODULE graphs.partition;
+
+DROP TABLE IF EXISTS _heap_graph_dominator_tree_for_partition;
+CREATE PERFETTO TABLE _heap_graph_dominator_tree_for_partition AS
+SELECT
+ tree.id,
+ tree.idom_id as parent_id,
+ obj.type_id as group_key
+FROM memory_heap_graph_dominator_tree tree
+JOIN heap_graph_object obj USING(id)
+UNION ALL
+-- tree partition below requires a single root.
+SELECT
+ memory_heap_graph_super_root_fn() AS id,
+ NULL AS parent_id,
+ (SELECT MAX(id) + 1 FROM heap_graph_class) AS group_key;
+
+DROP TABLE IF EXISTS _heap_object_marked_for_dominated_stats;
+CREATE PERFETTO TABLE _heap_object_marked_for_dominated_stats AS
+SELECT
+ id,
+ IIF(parent_id IS NULL, 1, 0) as marked
+FROM tree_structural_partition_by_group!(_heap_graph_dominator_tree_for_partition)
+ORDER BY id;
+
+DROP TABLE IF EXISTS _heap_class_stats;
+CREATE PERFETTO TABLE _heap_class_stats AS
+SELECT
+ obj.upid,
+ obj.graph_sample_ts,
+ obj.type_id,
+ COUNT(1) AS obj_count,
+ SUM(self_size) AS size_bytes,
+ SUM(native_size) AS native_size_bytes,
+ SUM(IIF(obj.reachable, 1, 0)) AS reachable_obj_count,
+ SUM(IIF(obj.reachable, self_size, 0)) AS reachable_size_bytes,
+ SUM(IIF(obj.reachable, native_size, 0)) AS reachable_native_size_bytes,
+ SUM(IIF(marked, dominated_obj_count, 0)) AS dominated_obj_count,
+ SUM(IIF(marked, dominated_size_bytes, 0)) AS dominated_size_bytes,
+ SUM(IIF(marked, dominated_native_size_bytes, 0)) AS dominated_native_size_bytes
+FROM heap_graph_object obj
+-- Left joins to preserve unreachable objects.
+LEFT JOIN _heap_object_marked_for_dominated_stats USING(id)
+LEFT JOIN memory_heap_graph_dominator_tree USING(id)
+GROUP BY 1, 2, 3
+ORDER BY 1, 2, 3;
+
+DROP VIEW IF EXISTS java_heap_class_stats_output;
+CREATE PERFETTO VIEW java_heap_class_stats_output AS
+WITH
+-- Group by to build the repeated field by upid, ts
+heap_class_stats_count_protos AS (
+ SELECT
+ upid,
+ graph_sample_ts,
+ RepeatedField(JavaHeapClassStats_TypeCount(
+ 'type_name', IFNULL(c.deobfuscated_name, c.name),
+ 'obj_count', obj_count,
+ 'size_bytes', size_bytes,
+ 'native_size_bytes', native_size_bytes,
+ 'reachable_obj_count', reachable_obj_count,
+ 'reachable_size_bytes', reachable_size_bytes,
+ 'reachable_native_size_bytes', reachable_native_size_bytes,
+ 'dominated_obj_count', dominated_obj_count,
+ 'dominated_size_bytes', dominated_size_bytes,
+ 'dominated_native_size_bytes', dominated_native_size_bytes
+ )) AS count_protos
+ FROM _heap_class_stats s
+ JOIN heap_graph_class c ON s.type_id = c.id
+ GROUP BY 1, 2
+),
+-- Group by to build the repeated field by upid
+heap_class_stats_sample_protos AS (
+ SELECT
+ upid,
+ RepeatedField(JavaHeapClassStats_Sample(
+ 'ts', graph_sample_ts,
+ 'type_count', count_protos
+ )) AS sample_protos
+ FROM heap_class_stats_count_protos
+ GROUP BY 1
+)
+SELECT JavaHeapClassStats(
+ 'instance_stats', RepeatedField(JavaHeapClassStats_InstanceStats(
+ 'upid', upid,
+ 'process', process_metadata.metadata,
+ 'samples', sample_protos
+ )))
+FROM heap_class_stats_sample_protos JOIN process_metadata USING (upid);
diff --git a/test/trace_processor/diff_tests/metrics/profiling/java_heap_class_stats.out b/test/trace_processor/diff_tests/metrics/profiling/java_heap_class_stats.out
new file mode 100644
index 0000000..2a2a361
--- /dev/null
+++ b/test/trace_processor/diff_tests/metrics/profiling/java_heap_class_stats.out
@@ -0,0 +1,73 @@
+java_heap_class_stats {
+ instance_stats {
+ upid: 2
+ process {
+ name: "system_server"
+ uid: 1000
+ pid: 2
+ }
+ samples {
+ ts: 10
+ type_count {
+ type_name: "FactoryProducerDelegateImplActor"
+ obj_count: 1
+ size_bytes: 64
+ native_size_bytes: 0
+ reachable_obj_count: 1
+ reachable_size_bytes: 64
+ reachable_native_size_bytes: 0
+ dominated_obj_count: 2
+ dominated_size_bytes: 96
+ dominated_native_size_bytes: 0
+ }
+ type_count {
+ type_name: "Foo"
+ obj_count: 2
+ size_bytes: 160
+ native_size_bytes: 0
+ reachable_obj_count: 1
+ reachable_size_bytes: 32
+ reachable_native_size_bytes: 0
+ dominated_obj_count: 1
+ dominated_size_bytes: 32
+ dominated_native_size_bytes: 0
+ }
+ type_count {
+ type_name: "DeobfuscatedA"
+ obj_count: 1
+ size_bytes: 1024
+ native_size_bytes: 0
+ reachable_obj_count: 0
+ reachable_size_bytes: 0
+ reachable_native_size_bytes: 0
+ dominated_obj_count: 0
+ dominated_size_bytes: 0
+ dominated_native_size_bytes: 0
+ }
+ type_count {
+ type_name: "DeobfuscatedA[]"
+ obj_count: 1
+ size_bytes: 256
+ native_size_bytes: 0
+ reachable_obj_count: 1
+ reachable_size_bytes: 256
+ reachable_native_size_bytes: 0
+ dominated_obj_count: 1
+ dominated_size_bytes: 256
+ dominated_native_size_bytes: 0
+ }
+ type_count {
+ type_name: "java.lang.Class<DeobfuscatedA[]>"
+ obj_count: 1
+ size_bytes: 256
+ native_size_bytes: 0
+ reachable_obj_count: 0
+ reachable_size_bytes: 0
+ reachable_native_size_bytes: 0
+ dominated_obj_count: 0
+ dominated_size_bytes: 0
+ dominated_native_size_bytes: 0
+ }
+ }
+ }
+}
diff --git a/test/trace_processor/diff_tests/metrics/profiling/tests.py b/test/trace_processor/diff_tests/metrics/profiling/tests.py
index 9a4ff07..21cb826 100644
--- a/test/trace_processor/diff_tests/metrics/profiling/tests.py
+++ b/test/trace_processor/diff_tests/metrics/profiling/tests.py
@@ -109,3 +109,9 @@
trace=Path('heap_graph.textproto'),
query=Metric('java_heap_histogram'),
out=Path('java_heap_histogram.out'))
+
+ def test_java_heap_class_stats(self):
+ return DiffTestBlueprint(
+ trace=Path('heap_graph.textproto'),
+ query=Metric('java_heap_class_stats'),
+ out=Path('java_heap_class_stats.out'))