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