perfetto: allow building metrics protos in Java using Android
This unblocks work integrating Perfetto into tradefed.
Change-Id: I32de25b4296df8b186cd553bdba8289b8303dc10
diff --git a/Android.bp b/Android.bp
index 0d5f5e6..95b241f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7298,6 +7298,16 @@
],
}
+java_library_host {
+ name: "perfetto_metrics-full",
+ proto: {
+ type: "full",
+ },
+ srcs: [
+ "protos/perfetto/metrics/perfetto_merged_metrics.proto",
+ ],
+}
+
// This sample target shows how to use the perfetto client API from within the
// Android tree.
cc_binary {
diff --git a/Android.bp.extras b/Android.bp.extras
index ec61a44..9586516 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -29,6 +29,16 @@
],
}
+java_library_host {
+ name: "perfetto_metrics-full",
+ proto: {
+ type: "full",
+ },
+ srcs: [
+ "protos/perfetto/metrics/perfetto_merged_metrics.proto",
+ ],
+}
+
// This sample target shows how to use the perfetto client API from within the
// Android tree.
cc_binary {
diff --git a/protos/perfetto/metrics/android/batt_metric.proto b/protos/perfetto/metrics/android/batt_metric.proto
index 54e6a8b..b7c2d15 100644
--- a/protos/perfetto/metrics/android/batt_metric.proto
+++ b/protos/perfetto/metrics/android/batt_metric.proto
@@ -30,7 +30,7 @@
optional double current_avg_ua = 5;
}
- // Battery counters info for each ts of the trace. This should only be
- // extracted for short traces.
+ // Battery counters info for each ts of the trace. This should only be
+ // extracted for short traces.
repeated BatteryCounters battery_counters = 1;
}
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
new file mode 100644
index 0000000..fc45d52
--- /dev/null
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -0,0 +1,534 @@
+// AUTOGENERATED - DO NOT EDIT
+// ---------------------------
+// This file has been generated by
+// AOSP://external/perfetto/tools/gen_merged_protos
+// merging the perfetto config protos.
+// This fused proto is intended to be copied in:
+// - Android tree, for statsd.
+// - Google internal repos.
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+// Begin of protos/perfetto/metrics/android/batt_metric.proto
+
+message AndroidBatteryMetric {
+ message BatteryCounters {
+ // Timestamp measured from boot time [ns].
+ optional int64 timestamp_ns = 1;
+ // Fields 2-5 are the same as in BatteryCounters proto in TracePacket.
+ optional double charge_counter_uah = 2;
+ optional float capacity_percent = 3;
+ optional double current_ua = 4;
+ optional double current_avg_ua = 5;
+ }
+
+ // Battery counters info for each ts of the trace. This should only be
+ // extracted for short traces.
+ repeated BatteryCounters battery_counters = 1;
+}
+
+// End of protos/perfetto/metrics/android/batt_metric.proto
+
+// Begin of protos/perfetto/metrics/android/cpu_metric.proto
+
+message AndroidCpuMetric {
+ // Next id: 6
+ message Metrics {
+ // CPU megacycles (i.e. cycles divided by 1e6).
+ optional int64 mcycles = 1;
+
+ // Total time the thread was running for this breakdown in
+ // nanoseconds.
+ optional int64 runtime_ns = 2;
+
+ // Min/max/average CPU frequency weighted by the time the CPU was
+ // running at each frequency in this breakdown.
+ optional int64 min_freq_khz = 3;
+ optional int64 max_freq_khz = 4;
+ optional int64 avg_freq_khz = 5;
+ }
+
+ // Next id: 7
+ message CoreData {
+ optional uint32 id = 1;
+ optional Metrics metrics = 6;
+
+ reserved 2 to 5;
+ }
+
+ // Next id: 3
+ message CoreTypeData {
+ optional string type = 1;
+ optional Metrics metrics = 2;
+ }
+
+ // Next id: 7
+ message Thread {
+ optional string name = 1;
+ optional Metrics metrics = 4;
+
+ // Breakdowns of above metrics.
+ repeated CoreData core = 2;
+ repeated CoreTypeData core_type = 5;
+
+ reserved 3;
+ }
+
+ // Next id: 4
+ message Process {
+ optional string name = 1;
+ optional Metrics metrics = 4;
+
+ // Breakdowns of above metrics.
+ repeated Thread threads = 2;
+
+ reserved 3;
+ }
+
+ repeated Process process_info = 1;
+}
+
+// End of protos/perfetto/metrics/android/cpu_metric.proto
+
+// Begin of protos/perfetto/metrics/android/mem_metric.proto
+
+// Memory metrics on Android.
+message AndroidMemoryMetric {
+ message ProcessMetrics {
+ optional string process_name = 1;
+ optional ProcessMemoryCounters total_counters = 2;
+ repeated PriorityBreakdown priority_breakdown = 3;
+ }
+
+ message PriorityBreakdown {
+ optional string priority = 1;
+ optional ProcessMemoryCounters counters = 2;
+ }
+
+ message ProcessMemoryCounters {
+ optional Counter anon_rss = 1;
+ optional Counter file_rss = 2;
+ optional Counter swap = 3;
+ optional Counter anon_and_swap = 4;
+
+ // Available when ART trace events are available.
+ optional Counter java_heap = 5;
+ }
+
+ message Counter {
+ optional double min = 1;
+ optional double max = 2;
+ optional double avg = 3;
+ }
+
+ // Process metrics, grouped by process name
+ repeated ProcessMetrics process_metrics = 1;
+}
+
+// End of protos/perfetto/metrics/android/mem_metric.proto
+
+// Begin of protos/perfetto/metrics/android/mem_unagg_metric.proto
+
+// Unaggregated memory metrics on Android.
+message AndroidMemoryUnaggregatedMetric {
+ message ProcessValues {
+ optional string process_name = 1;
+ optional ProcessMemoryValues mem_values = 2;
+ }
+
+ message ProcessMemoryValues {
+ repeated Value anon_rss = 1;
+ repeated Value file_rss = 2;
+ repeated Value swap = 3;
+ repeated Value anon_and_swap = 4;
+ }
+
+ message Value {
+ optional int64 ts = 1;
+ optional int32 oom_score = 2;
+ optional double value = 3;
+ }
+
+ // Process metrics for every process instance in trace.
+ repeated ProcessValues process_values = 1;
+}
+
+// End of protos/perfetto/metrics/android/mem_unagg_metric.proto
+
+// Begin of protos/perfetto/metrics/android/ion_metric.proto
+
+// ion memory stats on Android.
+message AndroidIonMetric {
+ message Buffer {
+ optional string name = 1;
+ optional double avg_size_bytes = 2;
+ optional double min_size_bytes = 3;
+ optional double max_size_bytes = 4;
+ }
+
+ repeated Buffer buffer = 1;
+}
+
+// End of protos/perfetto/metrics/android/ion_metric.proto
+
+// Begin of protos/perfetto/metrics/android/lmk_metric.proto
+
+// LMK stats on Android.
+message AndroidLmkMetric {
+ message ByOomScore {
+ optional int32 oom_score_adj = 1;
+ optional int32 count = 2;
+ }
+
+ // Total count of LMK events observed in the trace.
+ optional int32 total_count = 1;
+ repeated ByOomScore by_oom_score = 2;
+}
+
+// End of protos/perfetto/metrics/android/lmk_metric.proto
+
+// Begin of protos/perfetto/metrics/android/process_metadata.proto
+
+message AndroidProcessMetadata {
+ // Process name. Usually, cmdline or <package_name>(:<custom_name>)?.
+ optional string name = 1;
+
+ // User id under which this process runs.
+ optional int64 uid = 2;
+
+ // Package metadata from Android package list.
+ message Package {
+ optional string package_name = 1;
+ optional int64 apk_version_code = 2;
+ optional bool debuggable = 3;
+ }
+
+ // Package that this process belongs to.
+ //
+ // If this process shares its uid (see `packages_for_uid` field), the package
+ // is determined based on the process name and package name. If there is no
+ // match this field is empty.
+ optional Package package = 7;
+
+ // All packages using this uid.
+ //
+ // Shared uid documentation:
+ // https://developer.android.com/guide/topics/manifest/manifest-element#uid
+ repeated Package packages_for_uid = 8;
+
+ reserved 3, 4, 5, 6;
+}
+
+// End of protos/perfetto/metrics/android/process_metadata.proto
+
+// Begin of protos/perfetto/metrics/android/lmk_reason_metric.proto
+
+// Potential culplit of a low-memory kill on Android.
+message AndroidLmkReasonMetric {
+ message Process {
+ optional AndroidProcessMetadata process = 1;
+
+ // OOM score adj of the process.
+ optional int32 oom_score_adj = 2;
+
+ // RSS + swap.
+ optional int64 size = 3;
+ }
+ message Lmk {
+ // OOM score adj of the LMK'ed process.
+ optional int32 oom_score_adj = 1;
+
+ // Total size of the system ION heap in bytes during this LMK.
+ optional int64 system_ion_heap_size = 2;
+
+ // Processes present during this LMK.
+ repeated Process processes = 3;
+ }
+
+ repeated Lmk lmks = 1;
+}
+
+// End of protos/perfetto/metrics/android/lmk_reason_metric.proto
+
+// Begin of protos/perfetto/metrics/android/powrails_metric.proto
+
+message AndroidPowerRails {
+ // Energy data per Power Rail at given ts.
+ message EnergyData {
+ // Time since device boot(CLOCK_BOTTOMTIME) in milli-seconds.
+ optional int64 timestamp_ms = 1;
+ // Accumulated energy since device boot in microwatt-seconds(uws).
+ optional double energy_uws = 2;
+ }
+
+ message PowerRails {
+ // Name of the rail.
+ optional string name = 1;
+ // Energy data for given rail and for all samples in the trace.
+ repeated EnergyData energy_data = 2;
+ }
+
+ // Energy data per Power Rail.
+ repeated PowerRails power_rails = 1;
+}
+// End of protos/perfetto/metrics/android/powrails_metric.proto
+
+// Begin of protos/perfetto/metrics/android/startup_metric.proto
+
+// Android app startup metrics.
+message AndroidStartupMetric {
+ // A simplified view of the task state durations for a thread
+ // and a span of time.
+ message TaskStateBreakdown {
+ optional int64 running_dur_ns = 1;
+ optional int64 runnable_dur_ns = 2;
+ optional int64 uninterruptible_sleep_dur_ns = 3;
+ optional int64 interruptible_sleep_dur_ns = 4;
+ }
+
+ message Slice { optional int64 dur_ns = 1; }
+
+ // Timing information spanning the intent received by the
+ // activity manager to the first frame drawn.
+ // All times and durations in nanoseconds (ns).
+ message ToFirstFrame {
+ optional int64 dur_ns = 1;
+ optional TaskStateBreakdown main_thread_by_task_state = 2;
+
+ // In this timespan, how many processes (apart from the main activity) were
+ // spawned.
+ optional uint32 other_processes_spawned_count = 3;
+
+ // Total time spent in activity manager between the initial intent
+ // and the end of the activity starter.
+ optional Slice time_activity_manager = 4;
+
+ // The following slices follow the typical steps post-fork.
+ optional Slice time_activity_thread_main = 5;
+ optional Slice time_bind_application = 6;
+ optional Slice time_activity_start = 7;
+ optional Slice time_activity_resume = 8;
+ optional Slice time_choreographer = 9;
+
+ // If we are starting a new process, record the duration from the
+ // intent being received to the time we call the zygote.
+ optional Slice time_before_start_process = 10;
+
+ // The actual duration of the process start (based on the zygote slice).
+ optional Slice time_during_start_process = 11;
+
+ // The ratio between the cpu time of the activity process
+ // to all other processes in the system.
+ optional double other_process_to_activity_cpu_ratio = 12;
+ }
+
+ // Next id: 7
+ message Startup {
+ // Random id uniquely identifying an app startup in this trace.
+ optional uint32 startup_id = 1;
+
+ // Name of the package launched
+ optional string package_name = 2;
+
+ // Name of the process launched
+ optional string process_name = 3;
+
+ // Did we ask the zygote for a new process
+ optional bool zygote_new_process = 4;
+
+ // Number of processes hosting the activity involved in the launch.
+ // This will usually be 1. If it is 0, it is indicative of a data / process
+ // error. If > 1, the process died during startup and the system respawned
+ // it.
+ optional uint32 activity_hosting_process_count = 6;
+
+ optional ToFirstFrame to_first_frame = 5;
+ }
+
+ repeated Startup startup = 1;
+}
+
+// End of protos/perfetto/metrics/android/startup_metric.proto
+
+// Begin of protos/perfetto/metrics/android/heap_profile_callsites.proto
+
+message HeapProfileCallsites {
+ message Frame {
+ optional string name = 1;
+ optional string mapping_name = 2;
+ }
+
+ message Counters {
+ // Count of objects allocated
+ optional int64 total_count = 1;
+ // Count of bytes allocated
+ optional int64 total_bytes = 2;
+
+ // Count of allocated objects that were not freed
+ optional int64 delta_count = 3;
+ // Count of allocated bytes that were not freed
+ optional int64 delta_bytes = 4;
+ }
+
+ message Callsite {
+ // The hash unambiguously identifies a callsite in a heap profile (as a
+ // traversal from the root node). It is based on the symbol names (instead
+ // of the addresses).
+ optional int64 hash = 1;
+ optional int64 parent_hash = 2;
+
+ // Leaf frame of the callsite. Use parent_hash to traverse to parent nodes.
+ optional Frame frame = 3;
+
+ optional Counters self_allocs = 4;
+ optional Counters child_allocs = 5;
+ }
+
+ // Callsites per process instance.
+ // Next id: 7
+ message InstanceStats {
+ optional uint32 pid = 1;
+ // TODO(ilkos): Remove process_name in favour of the metadata.
+ optional string process_name = 2;
+ optional AndroidProcessMetadata process = 6;
+ repeated Callsite callsites = 3;
+
+ // Bytes allocated via malloc but not freed.
+ optional int64 profile_delta_bytes = 4;
+ // Bytes allocated via malloc irrespective of whether they were freed.
+ optional int64 profile_total_bytes = 5;
+ }
+
+ repeated InstanceStats instance_stats = 1;
+}
+
+// End of protos/perfetto/metrics/android/heap_profile_callsites.proto
+
+// Begin of protos/perfetto/metrics/android/package_list.proto
+
+message AndroidPackageList {
+ message Package {
+ optional string package_name = 1;
+ optional int64 uid = 2;
+ optional int64 version_code = 3;
+ }
+
+ repeated Package packages = 1;
+}
+
+// End of protos/perfetto/metrics/android/package_list.proto
+
+// Begin of protos/perfetto/metrics/android/unsymbolized_frames.proto
+
+message UnsymbolizedFrames {
+ message Frame {
+ optional string module = 1;
+ optional string build_id = 2;
+ optional int64 address = 3;
+ }
+
+ repeated Frame frames = 1;
+}
+
+// End of protos/perfetto/metrics/android/unsymbolized_frames.proto
+
+// Begin of protos/perfetto/metrics/android/java_heap_stats.proto
+
+message JavaHeapStats {
+ message Sample {
+ optional int64 ts = 1;
+ optional int64 heap_size = 2;
+ optional int64 reachable_heap_size = 3;
+ }
+
+ // Heap stats per process. One sample per dump (can be > 1 if continuous
+ // dump is enabled).
+ message InstanceStats {
+ optional uint32 upid = 1;
+ optional AndroidProcessMetadata process = 2;
+ repeated Sample samples = 3;
+ }
+
+ repeated InstanceStats instance_stats = 1;
+}
+
+// End of protos/perfetto/metrics/android/java_heap_stats.proto
+
+// Begin of protos/perfetto/metrics/metrics.proto
+
+// Trace processor metadata
+message TraceMetadata {
+ message Entry {
+ optional string name = 1;
+ optional uint32 idx = 2;
+ optional int64 value = 3;
+ }
+
+ repeated Entry error_stats_entry = 1;
+ optional int64 trace_duration_ns = 2;
+ optional string trace_uuid = 3;
+ optional string android_build_fingerprint = 4;
+ optional int64 statsd_triggering_subscription_id = 5;
+}
+
+// Root message for all Perfetto-based metrics.
+//
+// Next id: 19
+message TraceMetrics {
+ reserved 4, 10, 13, 14;
+
+ // Battery counters metric on Android.
+ optional AndroidBatteryMetric android_batt = 5;
+
+ // CPU usage per trace, process and thread.
+ optional AndroidCpuMetric android_cpu = 6;
+
+ // Memory metrics on Android (owned by the Android Telemetry team).
+ optional AndroidMemoryMetric android_mem = 1;
+
+ // Memory metrics on Android in unaggregated form. (owned by the Android
+ // Telemetry team).
+ // Note: this generates a lot of data so should not be requested unless it
+ // is clear that this data is necessary.
+ optional AndroidMemoryUnaggregatedMetric android_mem_unagg = 11;
+
+ // Package list.
+ optional AndroidPackageList android_package_list = 12;
+
+ // ion buffer memory metrics.
+ optional AndroidIonMetric android_ion = 9;
+
+ // Statistics about low memory kills.
+ optional AndroidLmkMetric android_lmk = 8;
+
+ // Power Rails metrics on Android.
+ optional AndroidPowerRails android_powrails = 7;
+
+ // Startup metrics on Android (owned by the Android Telemetry team).
+ optional AndroidStartupMetric android_startup = 2;
+
+ // Heap profiler callsite statistics.
+ optional HeapProfileCallsites heap_profile_callsites = 16;
+
+ // Trace metadata (applicable to all traces).
+ optional TraceMetadata trace_metadata = 3;
+
+ // Returns stack frames missing symbols.
+ optional UnsymbolizedFrames unsymbolized_frames = 15;
+
+ // If the trace contains a heap graph, output allocation statistics.
+ optional JavaHeapStats java_heap_stats = 17;
+
+ // Metrics used to find potential culprits of low-memory kills.
+ optional AndroidLmkReasonMetric android_lmk_reason = 18;
+
+ // Demo extensions.
+ extensions 450 to 499;
+
+ // Vendor extensions.
+ extensions 500 to 1000;
+}
+
+// End of protos/perfetto/metrics/metrics.proto
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index 1dee4a7..d8a8f7f 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -117,6 +117,12 @@
MERGED_TRACE_PROTO = 'protos/perfetto/trace/perfetto_trace.proto'
+METRICS_PROTOS = (
+ 'protos/perfetto/metrics/metrics.proto',
+)
+
+MERGED_METRICS_PROTO = 'protos/perfetto/metrics/perfetto_merged_metrics.proto'
+
REPLACEMENT_HEADER = '''
// AUTOGENERATED - DO NOT EDIT
// ---------------------------
@@ -133,10 +139,14 @@
'''
-def merge_protos(proto_paths, output_path):
+def merge_protos_content(proto_paths, follow_imports, added_files, add_header):
root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
merged_content = ''
for proto in proto_paths:
+ if proto in added_files:
+ continue
+ added_files.add(proto)
+
path = os.path.join(root_dir, proto)
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
@@ -145,8 +155,14 @@
header = re.match(r'\/(\*|\/)(?:.|\s)*?package .*;\n', content)
header = header.group(0)
content = content[len(header):]
- if merged_content == '':
+ if merged_content == '' and add_header:
merged_content += REPLACEMENT_HEADER.lstrip() % __file__
+
+ if follow_imports:
+ matches = re.findall(r'^import "(.*)";\n\n?', content, flags=re.MULTILINE)
+ merged_content += merge_protos_content(
+ matches, follow_imports, added_files, add_header=False)
+
content = re.sub(r'^import.*?\n\n?', '', content, flags=re.MULTILINE)
merged_content += '\n// Begin of %s\n' % proto
merged_content += content
@@ -173,6 +189,13 @@
for before, after in substitutions:
merged_content = merged_content.replace(before, after)
+ return merged_content
+
+
+def merge_protos(proto_paths, output_path, follow_imports):
+ root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ merged_content = merge_protos_content(
+ proto_paths, follow_imports, added_files=set(), add_header=True)
out_path = os.path.join(root_dir, output_path)
prev_content = None
@@ -194,9 +217,10 @@
def main():
config_result = merge_protos(COMMON_PROTOS + CONFIG_PROTOS,
- MERGED_CONFIG_PROTO)
+ MERGED_CONFIG_PROTO, False)
trace_result = merge_protos(COMMON_PROTOS + TRACE_PROTOS + CONFIG_PROTOS,
- MERGED_TRACE_PROTO)
+ MERGED_TRACE_PROTO, False)
+ trace_result = merge_protos(METRICS_PROTOS, MERGED_METRICS_PROTO, True)
return 0 if config_result and trace_result else 1