Merge "trace_to_text: report overwrite events for ftrace"
diff --git a/gn/standalone/proto_library.gni b/gn/standalone/proto_library.gni
index 1401cc9..3005f78 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -38,6 +38,11 @@
     generate_cc = invoker.generate_cc
   }
 
+  generate_descriptor = ""
+  if (defined(invoker.generate_descriptor)) {
+    generate_descriptor = invoker.generate_descriptor
+  }
+
   if (defined(invoker.generator_plugin_label)) {
     plugin_host_label = invoker.generator_plugin_label + "($host_toolchain)"
     plugin_path = get_label_info(plugin_host_label, "root_out_dir") + "/" +
@@ -67,6 +72,10 @@
   protos = rebase_path(proto_sources, proto_in_dir)
   protogens = []
 
+  if (generate_descriptor != "") {
+    protogens += [ "$root_gen_dir/" + generate_descriptor ]
+  }
+
   foreach(proto, protos) {
     proto_dir = get_path_info(proto, "dir")
     proto_name = get_path_info(proto, "name")
@@ -115,6 +124,12 @@
         rel_cc_out_dir,
       ]
     }
+    if (generate_descriptor != "") {
+      args += [
+        "--descriptor_set_out",
+        rebase_path("$root_gen_dir/" + generate_descriptor, root_build_dir),
+      ]
+    }
 
     if (generate_with_plugin) {
       plugin_path_rebased = rebase_path(plugin_path, root_build_dir)
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index d58e354..90af5e0 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -55,6 +55,7 @@
     "ftrace:lite",
     "ps:lite",
   ]
+  generate_descriptor = "$perfetto_root_path/protos/trace/trace.descriptor"
   sources = proto_sources
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
diff --git a/src/ftrace_reader/BUILD.gn b/src/ftrace_reader/BUILD.gn
index 80823b4..f2e010e 100644
--- a/src/ftrace_reader/BUILD.gn
+++ b/src/ftrace_reader/BUILD.gn
@@ -147,7 +147,7 @@
   }
 }
 
-if (use_libfuzzer) {
+if (use_libfuzzer && !build_with_chromium) {
   executable("cpu_reader_fuzzer") {
     testonly = true
     sources = [
diff --git a/src/ipc/BUILD.gn b/src/ipc/BUILD.gn
index 69c320c..701565c 100644
--- a/src/ipc/BUILD.gn
+++ b/src/ipc/BUILD.gn
@@ -46,7 +46,7 @@
   ]
 }
 
-if (use_libfuzzer) {
+if (use_libfuzzer && !build_with_chromium) {
   executable("buffered_frame_deserializer_fuzzer") {
     sources = [
       "buffered_frame_deserializer_fuzzer.cc",
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 72e9c4a..a69e47b 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -45,7 +45,7 @@
   }
 }
 
-if (use_libfuzzer) {
+if (use_libfuzzer && !build_with_chromium) {
   executable("end_to_end_shared_memory_fuzzer") {
     sources = [
       "end_to_end_shared_memory_fuzzer.cc",
diff --git a/test/cts/end_to_end_integrationtest_cts.cc b/test/cts/end_to_end_integrationtest_cts.cc
index 4392271..d40d92f 100644
--- a/test/cts/end_to_end_integrationtest_cts.cc
+++ b/test/cts/end_to_end_integrationtest_cts.cc
@@ -15,6 +15,7 @@
  */
 
 #include <random>
+#include <sys/system_properties.h>
 
 #include "gtest/gtest.h"
 #include "perfetto/trace/test_event.pbzero.h"
@@ -32,6 +33,16 @@
 class PerfettoCtsTest : public ::testing::Test {
  protected:
   void TestMockProducer(const std::string& producer_name) {
+    // Filter out watches; they do not have the required infrastructure to run
+    // these tests.
+    char chars[PROP_VALUE_MAX + 1];
+    int ret = __system_property_get("ro.build.characteristics", chars);
+    ASSERT_GE(ret, 0);
+    std::string characteristics(chars);
+    if (characteristics.find("watch") != std::string::npos) {
+      return;
+    }
+
     base::TestTaskRunner task_runner;
 
     TestHelper helper(&task_runner);
diff --git a/test/end_to_end_benchmark.cc b/test/end_to_end_benchmark.cc
index 100674e..f6f3d3d 100644
--- a/test/end_to_end_benchmark.cc
+++ b/test/end_to_end_benchmark.cc
@@ -36,7 +36,7 @@
   return getenv("BENCHMARK_FUNCTIONAL_TEST_ONLY") != nullptr;
 }
 
-void BenchmarkCommon(benchmark::State& state) {
+void BenchmarkProducer(benchmark::State& state) {
   base::TestTaskRunner task_runner;
 
   TestHelper helper(&task_runner);
@@ -87,10 +87,10 @@
   uint64_t wall_ns =
       static_cast<uint64_t>(base::GetWallTimeNs().count()) - wall_start_ns;
 
-  state.counters["Pro CPU"] = benchmark::Counter(100.0 * producer_ns / wall_ns);
   state.counters["Ser CPU"] = benchmark::Counter(100.0 * service_ns / wall_ns);
   state.counters["Ser ns/m"] =
       benchmark::Counter(1.0 * service_ns / message_count);
+  state.counters["Pro CPU"] = benchmark::Counter(100.0 * producer_ns / wall_ns);
   state.SetBytesProcessed(iterations * message_bytes * message_count);
 
   // Read back the buffer just to check correctness.
@@ -110,7 +110,104 @@
   }
 }
 
-void SaturateCpuArgs(benchmark::internal::Benchmark* b) {
+static void BenchmarkConsumer(benchmark::State& state) {
+  base::TestTaskRunner task_runner;
+
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
+
+  FakeProducer* producer = helper.ConnectFakeProducer();
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+
+  static const uint32_t kBufferSizeBytes =
+      IsBenchmarkFunctionalOnly() ? 16 * 1024 : 2 * 1024 * 1024;
+  trace_config.add_buffers()->set_size_kb(kBufferSizeBytes / 1024);
+
+  static constexpr uint32_t kRandomSeed = 42;
+  uint32_t message_bytes = static_cast<uint32_t>(state.range(0));
+  uint32_t mb_per_s = static_cast<uint32_t>(state.range(1));
+  bool is_saturated_producer = mb_per_s == 0;
+
+  uint32_t message_count = kBufferSizeBytes / message_bytes;
+  uint32_t messages_per_s = mb_per_s * 1024 * 1024 / message_bytes;
+  uint32_t number_of_batches =
+      is_saturated_producer ? 0 : std::max(1u, message_count / messages_per_s);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.perfetto.FakeProducer");
+  ds_config->set_target_buffer(0);
+  ds_config->mutable_for_testing()->set_seed(kRandomSeed);
+  ds_config->mutable_for_testing()->set_message_count(message_count);
+  ds_config->mutable_for_testing()->set_message_size(message_bytes);
+  ds_config->mutable_for_testing()->set_max_messages_per_second(messages_per_s);
+
+  helper.StartTracing(trace_config);
+  helper.WaitForProducerEnabled();
+
+  uint64_t wall_start_ns = static_cast<uint64_t>(base::GetWallTimeNs().count());
+  uint64_t service_start_ns =
+      static_cast<uint64_t>(helper.service_thread()->GetThreadCPUTimeNs());
+  uint64_t consumer_start_ns =
+      static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
+  uint64_t read_time_taken_ns = 0;
+
+  uint64_t iterations = 0;
+  uint32_t counter = 0;
+  for (auto _ : state) {
+    auto cname = "produced.and.committed." + std::to_string(iterations++);
+    auto on_produced_and_committed = task_runner.CreateCheckpoint(cname);
+    producer->ProduceEventBatch(helper.WrapTask(on_produced_and_committed));
+
+    if (is_saturated_producer) {
+      // If the producer is running in saturated mode, wait until it flushes
+      // data.
+      task_runner.RunUntilCheckpoint(cname);
+
+      // Then time how long it takes to read back the data.
+      int64_t start = base::GetWallTimeNs().count();
+      helper.ReadData(counter);
+      helper.WaitForReadData(counter++);
+      read_time_taken_ns +=
+          static_cast<uint64_t>(base::GetWallTimeNs().count() - start);
+    } else {
+      // If the producer is not running in saturated mode, every second the
+      // producer will send a batch of data over. Wait for a second before
+      // performing readback; do this for each batch the producer sends.
+      for (uint32_t i = 0; i < number_of_batches; i++) {
+        auto batch_cname = "batch.checkpoint." + std::to_string(counter);
+        auto batch_checkpoint = task_runner.CreateCheckpoint(batch_cname);
+        task_runner.PostDelayedTask(batch_checkpoint, 1000);
+        task_runner.RunUntilCheckpoint(batch_cname);
+
+        int64_t start = base::GetWallTimeNs().count();
+        helper.ReadData(counter);
+        helper.WaitForReadData(counter++);
+        read_time_taken_ns +=
+            static_cast<uint64_t>(base::GetWallTimeNs().count() - start);
+      }
+    }
+  }
+  uint64_t service_ns =
+      helper.service_thread()->GetThreadCPUTimeNs() - service_start_ns;
+  uint64_t consumer_ns =
+      static_cast<uint64_t>(base::GetThreadCPUTimeNs().count()) -
+      consumer_start_ns;
+  uint64_t wall_ns =
+      static_cast<uint64_t>(base::GetWallTimeNs().count()) - wall_start_ns;
+
+  state.counters["Ser CPU"] = benchmark::Counter(100.0 * service_ns / wall_ns);
+  state.counters["Ser ns/m"] =
+      benchmark::Counter(1.0 * service_ns / message_count);
+  state.counters["Con CPU"] = benchmark::Counter(100.0 * consumer_ns / wall_ns);
+  state.counters["Con Speed"] =
+      benchmark::Counter(iterations * 1000.0 * 1000 * 1000 * kBufferSizeBytes /
+                         read_time_taken_ns);
+}
+
+void SaturateCpuProducerArgs(benchmark::internal::Benchmark* b) {
   int min_message_count = 16;
   int max_message_count = IsBenchmarkFunctionalOnly() ? 1024 : 1024 * 1024;
   int min_payload = 8;
@@ -122,7 +219,7 @@
   }
 }
 
-void ConstantRateArgs(benchmark::internal::Benchmark* b) {
+void ConstantRateProducerArgs(benchmark::internal::Benchmark* b) {
   int message_count = IsBenchmarkFunctionalOnly() ? 2 * 1024 : 128 * 1024;
   int min_speed = IsBenchmarkFunctionalOnly() ? 64 : 8;
   int max_speed = IsBenchmarkFunctionalOnly() ? 128 : 128;
@@ -131,23 +228,60 @@
     b->Args({message_count, 256, speed});
   }
 }
+
+void SaturateCpuConsumerArgs(benchmark::internal::Benchmark* b) {
+  int min_payload = 8;
+  int max_payload = IsBenchmarkFunctionalOnly() ? 16 : 64 * 1024;
+  for (int bytes = min_payload; bytes <= max_payload; bytes *= 2) {
+    b->Args({bytes, 0 /* speed */});
+  }
+}
+
+void ConstantRateConsumerArgs(benchmark::internal::Benchmark* b) {
+  int min_speed = IsBenchmarkFunctionalOnly() ? 128 : 1;
+  int max_speed = IsBenchmarkFunctionalOnly() ? 128 : 2;
+  for (int speed = min_speed; speed <= max_speed; speed *= 2) {
+    b->Args({2, speed});
+    b->Args({4, speed});
+  }
+}
+
 }  // namespace
 
-static void BM_EndToEnd_SaturateCpu(benchmark::State& state) {
-  BenchmarkCommon(state);
+static void BM_EndToEnd_Producer_SaturateCpu(benchmark::State& state) {
+  BenchmarkProducer(state);
 }
 
-BENCHMARK(BM_EndToEnd_SaturateCpu)
+BENCHMARK(BM_EndToEnd_Producer_SaturateCpu)
     ->Unit(benchmark::kMicrosecond)
     ->UseRealTime()
-    ->Apply(SaturateCpuArgs);
+    ->Apply(SaturateCpuProducerArgs);
 
-static void BM_EndToEnd_ConstantRate(benchmark::State& state) {
-  BenchmarkCommon(state);
+static void BM_EndToEnd_Producer_ConstantRate(benchmark::State& state) {
+  BenchmarkProducer(state);
 }
 
-BENCHMARK(BM_EndToEnd_ConstantRate)
+BENCHMARK(BM_EndToEnd_Producer_ConstantRate)
     ->Unit(benchmark::kMicrosecond)
     ->UseRealTime()
-    ->Apply(ConstantRateArgs);
+    ->Apply(ConstantRateProducerArgs);
+
+static void BM_EndToEnd_Consumer_SaturateCpu(benchmark::State& state) {
+  BenchmarkConsumer(state);
+}
+
+BENCHMARK(BM_EndToEnd_Consumer_SaturateCpu)
+    ->Unit(benchmark::kMicrosecond)
+    ->UseRealTime()
+    ->Apply(SaturateCpuConsumerArgs);
+
+static void BM_EndToEnd_Consumer_ConstantRate(benchmark::State& state) {
+  BenchmarkConsumer(state);
+}
+
+BENCHMARK(BM_EndToEnd_Consumer_ConstantRate)
+    ->Unit(benchmark::kMillisecond)
+    ->UseRealTime()
+    ->Apply(ConstantRateConsumerArgs);
+
 }  // namespace perfetto
diff --git a/test/fake_producer.cc b/test/fake_producer.cc
index f2922d6..823759c 100644
--- a/test/fake_producer.cc
+++ b/test/fake_producer.cc
@@ -107,11 +107,12 @@
         handle->set_for_testing()->set_str(payload.get(), message_size_);
       }
       messages_to_emit -= messages_in_minibatch;
+      iterations++;
 
       // Pause until the second boundary to make sure that we are adhering to
       // the speed limitation.
       if (max_messages_per_second_ > 0) {
-        int64_t expected_time_taken = ++iterations * 1000;
+        int64_t expected_time_taken = iterations * 1000;
         base::TimeMillis time_taken = base::GetWallTimeMs() - start;
         while (time_taken.count() < expected_time_taken) {
           usleep(static_cast<useconds_t>(
@@ -119,8 +120,8 @@
           time_taken = base::GetWallTimeMs() - start;
         }
       }
+      trace_writer_->Flush(messages_to_emit > 0 ? [] {} : callback);
     }
-    trace_writer_->Flush(callback);
   });
 }
 
diff --git a/test/test_helper.cc b/test/test_helper.cc
index e5b78c9..55fa082 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -100,9 +100,9 @@
   endpoint_->EnableTracing(config);
 }
 
-void TestHelper::ReadData() {
-  on_packets_finished_callback_ =
-      task_runner_->CreateCheckpoint("readback.complete");
+void TestHelper::ReadData(uint32_t read_count) {
+  on_packets_finished_callback_ = task_runner_->CreateCheckpoint(
+      "readback.complete." + std::to_string(read_count));
   endpoint_->ReadBuffers();
 }
 
@@ -118,8 +118,9 @@
   task_runner_->RunUntilCheckpoint("stop.tracing");
 }
 
-void TestHelper::WaitForReadData() {
-  task_runner_->RunUntilCheckpoint("readback.complete");
+void TestHelper::WaitForReadData(uint32_t read_count) {
+  task_runner_->RunUntilCheckpoint("readback.complete." +
+                                   std::to_string(read_count));
 }
 
 std::function<void()> TestHelper::WrapTask(
diff --git a/test/test_helper.h b/test/test_helper.h
index b26a88d..d7afc86 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -43,12 +43,12 @@
   FakeProducer* ConnectFakeProducer();
   void ConnectConsumer();
   void StartTracing(const TraceConfig& config);
-  void ReadData();
+  void ReadData(uint32_t read_count = 0);
 
   void WaitForConsumerConnect();
   void WaitForProducerEnabled();
   void WaitForTracingDisabled();
-  void WaitForReadData();
+  void WaitForReadData(uint32_t read_count = 0);
 
   std::function<void()> WrapTask(const std::function<void()>& function);
 
diff --git a/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/tools/ftrace_proto_gen/ftrace_proto_gen.cc
index 1d94101..3c7e228 100644
--- a/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+++ b/tools/ftrace_proto_gen/ftrace_proto_gen.cc
@@ -16,12 +16,11 @@
 
 #include "tools/ftrace_proto_gen/ftrace_proto_gen.h"
 
-#include "perfetto/base/string_splitter.h"
-
 #include <fstream>
 #include <regex>
-#include <set>
-#include <string>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/string_splitter.h"
 
 namespace perfetto {
 
@@ -64,6 +63,64 @@
 
 }  // namespace
 
+ProtoType ProtoType::GetSigned() const {
+  PERFETTO_CHECK(type == NUMERIC);
+  if (is_signed)
+    return *this;
+
+  if (size == 64) {
+    return Numeric(64, true);
+  }
+
+  return Numeric(2 * size, true);
+}
+
+std::string ProtoType::ToString() const {
+  switch (type) {
+    case INVALID:
+      PERFETTO_CHECK(false);
+    case STRING:
+      return "string";
+    case NUMERIC: {
+      std::string s;
+      if (!is_signed)
+        s += "u";
+      s += "int";
+      s += std::to_string(size);
+      return s;
+    }
+  }
+  PERFETTO_CHECK(false);  // for GCC.
+}
+
+// static
+ProtoType ProtoType::String() {
+  return {STRING, 0, false};
+}
+
+// static
+ProtoType ProtoType::Invalid() {
+  return {INVALID, 0, false};
+}
+
+// static
+ProtoType ProtoType::Numeric(uint16_t size, bool is_signed) {
+  PERFETTO_CHECK(size == 32 || size == 64);
+  return {NUMERIC, size, is_signed};
+}
+
+ProtoType GetCommon(ProtoType one, ProtoType other) {
+  if (one.type == ProtoType::STRING || other.type == ProtoType::STRING)
+    return ProtoType::String();
+
+  if (one.is_signed || other.is_signed) {
+    one = one.GetSigned();
+    other = other.GetSigned();
+  }
+
+  return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed);
+}
+
 std::vector<std::string> GetFileLines(const std::string& filename) {
   std::string line;
   std::vector<std::string> lines;
@@ -80,44 +137,40 @@
   return lines;
 }
 
-std::string InferProtoType(const FtraceEvent::Field& field) {
+ProtoType InferProtoType(const FtraceEvent::Field& field) {
   // Fixed length strings: "char foo[16]"
   if (std::regex_match(field.type_and_name, std::regex(R"(char \w+\[\d+\])")))
-    return "string";
+    return ProtoType::String();
 
   // String pointers: "__data_loc char[] foo" (as in
   // 'cpufreq_interactive_boost').
   if (Contains(field.type_and_name, "char[] "))
-    return "string";
+    return ProtoType::String();
   if (Contains(field.type_and_name, "char * "))
-    return "string";
+    return ProtoType::String();
 
   // Variable length strings: "char* foo"
   if (StartsWith(field.type_and_name, "char *"))
-    return "string";
+    return ProtoType::String();
 
   // Variable length strings: "char foo" + size: 0 (as in 'print').
   if (StartsWith(field.type_and_name, "char ") && field.size == 0)
-    return "string";
+    return ProtoType::String();
 
   // ino_t, i_ino and dev_t are 32bit on some devices 64bit on others. For the
   // protos we need to choose the largest possible size.
   if (StartsWith(field.type_and_name, "ino_t ") ||
       StartsWith(field.type_and_name, "i_ino ") ||
       StartsWith(field.type_and_name, "dev_t ")) {
-    return "uint64";
+    return ProtoType::Numeric(64, /* is_signed= */ false);
   }
 
   // Ints of various sizes:
-  if (field.size <= 4 && field.is_signed)
-    return "int32";
-  if (field.size <= 4 && !field.is_signed)
-    return "uint32";
-  if (field.size <= 8 && field.is_signed)
-    return "int64";
-  if (field.size <= 8 && !field.is_signed)
-    return "uint64";
-  return "";
+  if (field.size <= 4)
+    return ProtoType::Numeric(32, field.is_signed);
+  if (field.size <= 8)
+    return ProtoType::Numeric(64, field.is_signed);
+  return ProtoType::Invalid();
 }
 
 void PrintEventFormatterMain(const std::set<std::string>& events) {
@@ -177,11 +230,12 @@
     if (name == "" || seen.count(name))
       continue;
     seen.insert(name);
-    std::string type = InferProtoType(field);
+    ProtoType type = InferProtoType(field);
     // Check we managed to infer a type.
-    if (type == "")
+    if (type.type == ProtoType::INVALID)
       continue;
-    proto_out->fields.emplace_back(Proto::Field{type, name, i});
+    proto_out->fields.emplace_back(
+        Proto::Field{std::move(type), std::move(name), i});
     i++;
   }
 
@@ -260,8 +314,8 @@
 
   for (const auto& field : proto.fields) {
     s += "    event->fields.push_back(MakeField(\"" + field.name + "\", " +
-         std::to_string(field.number) + ", kProto" + ToCamelCase(field.type) +
-         "));\n";
+         std::to_string(field.number) + ", kProto" +
+         ToCamelCase(field.type.ToString()) + "));\n";
   }
   return s;
 }
@@ -321,7 +375,7 @@
 
   s += "message " + name + " {\n";
   for (const Proto::Field& field : fields) {
-    s += "  optional " + field.type + " " + field.name + " = " +
+    s += "  optional " + field.type.ToString() + " " + field.name + " = " +
          std::to_string(field.number) + ";\n";
   }
   s += "}\n";
diff --git a/tools/ftrace_proto_gen/ftrace_proto_gen.h b/tools/ftrace_proto_gen/ftrace_proto_gen.h
index 86e834b..f0c9a38 100644
--- a/tools/ftrace_proto_gen/ftrace_proto_gen.h
+++ b/tools/ftrace_proto_gen/ftrace_proto_gen.h
@@ -25,9 +25,23 @@
 
 namespace perfetto {
 
+struct ProtoType {
+  enum Type { INVALID, NUMERIC, STRING };
+  Type type;
+  uint16_t size;
+  bool is_signed;
+
+  ProtoType GetSigned() const;
+  std::string ToString() const;
+
+  static ProtoType Invalid();
+  static ProtoType String();
+  static ProtoType Numeric(uint16_t size, bool is_signed);
+};
+
 struct Proto {
   struct Field {
-    std::string type;
+    ProtoType type;
     std::string name;
     uint32_t number;
   };
@@ -37,6 +51,7 @@
   std::string ToString();
 };
 
+ProtoType GetCommon(ProtoType one, ProtoType other);
 void PrintFtraceEventProtoAdditions(const std::set<std::string>& events);
 void PrintEventFormatterMain(const std::set<std::string>& events);
 void PrintEventFormatterUsingStatements(const std::set<std::string>& events);
@@ -45,7 +60,7 @@
                            const perfetto::Proto& proto);
 
 bool GenerateProto(const FtraceEvent& format, Proto* proto_out);
-std::string InferProtoType(const FtraceEvent::Field& field);
+ProtoType InferProtoType(const FtraceEvent::Field& field);
 
 std::vector<std::string> GetFileLines(const std::string& filename);
 std::set<std::string> GetWhitelistedEvents(
diff --git a/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc b/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
index 943f133..8943e7b 100644
--- a/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
+++ b/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
@@ -22,26 +22,39 @@
 
 TEST(FtraceEventParserTest, InferProtoType) {
   using Field = FtraceEvent::Field;
-  EXPECT_EQ(InferProtoType(Field{"char foo[16]", 0, 16, false}), "string");
-  EXPECT_EQ(InferProtoType(Field{"char bar_42[64]", 0, 64, false}), "string");
-  EXPECT_EQ(InferProtoType(Field{"__data_loc char[] foo", 0, 4, false}),
+  EXPECT_EQ(InferProtoType(Field{"char foo[16]", 0, 16, false}).ToString(),
             "string");
-  EXPECT_EQ(InferProtoType(Field{"char[] foo", 0, 8, false}), "string");
-  EXPECT_EQ(InferProtoType(Field{"char * foo", 0, 8, false}), "string");
+  EXPECT_EQ(InferProtoType(Field{"char bar_42[64]", 0, 64, false}).ToString(),
+            "string");
+  EXPECT_EQ(
+      InferProtoType(Field{"__data_loc char[] foo", 0, 4, false}).ToString(),
+      "string");
+  EXPECT_EQ(InferProtoType(Field{"char[] foo", 0, 8, false}).ToString(),
+            "string");
+  EXPECT_EQ(InferProtoType(Field{"char * foo", 0, 8, false}).ToString(),
+            "string");
 
-  EXPECT_EQ(InferProtoType(Field{"int foo", 0, 4, true}), "int32");
-  EXPECT_EQ(InferProtoType(Field{"s32 signal", 50, 4, true}), "int32");
+  EXPECT_EQ(InferProtoType(Field{"int foo", 0, 4, true}).ToString(), "int32");
+  EXPECT_EQ(InferProtoType(Field{"s32 signal", 50, 4, true}).ToString(),
+            "int32");
 
-  EXPECT_EQ(InferProtoType(Field{"unsigned int foo", 0, 4, false}), "uint32");
-  EXPECT_EQ(InferProtoType(Field{"u32 control_freq", 44, 4, false}), "uint32");
+  EXPECT_EQ(InferProtoType(Field{"unsigned int foo", 0, 4, false}).ToString(),
+            "uint32");
+  EXPECT_EQ(InferProtoType(Field{"u32 control_freq", 44, 4, false}).ToString(),
+            "uint32");
 
-  EXPECT_EQ(InferProtoType(Field{"ino_t foo", 0, 4, false}), "uint64");
-  EXPECT_EQ(InferProtoType(Field{"ino_t foo", 0, 8, false}), "uint64");
+  EXPECT_EQ(InferProtoType(Field{"ino_t foo", 0, 4, false}).ToString(),
+            "uint64");
+  EXPECT_EQ(InferProtoType(Field{"ino_t foo", 0, 8, false}).ToString(),
+            "uint64");
 
-  EXPECT_EQ(InferProtoType(Field{"dev_t foo", 0, 4, false}), "uint64");
-  EXPECT_EQ(InferProtoType(Field{"dev_t foo", 0, 8, false}), "uint64");
+  EXPECT_EQ(InferProtoType(Field{"dev_t foo", 0, 4, false}).ToString(),
+            "uint64");
+  EXPECT_EQ(InferProtoType(Field{"dev_t foo", 0, 8, false}).ToString(),
+            "uint64");
 
-  EXPECT_EQ(InferProtoType(Field{"char foo", 0, 0, false}), "string");
+  EXPECT_EQ(InferProtoType(Field{"char foo", 0, 0, false}).ToString(),
+            "string");
 }
 
 TEST(FtraceEventParserTest, GenerateProtoName) {
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index f71e001..d887501 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -368,6 +368,7 @@
     parser.add_argument('--cpp_out')
     parser.add_argument('--plugin')
     parser.add_argument('--plugin_out')
+    parser.add_argument('--descriptor_set_out')
     parser.add_argument('protos', nargs=argparse.REMAINDER)
     args = parser.parse_args(args[1:])