Make proto_to_cpp a build-time generator

This CL moves the generation of core classes derived from
protos (TraceConfig, DataSourceConfig, etc) at build time.
Previously these classes were checked in. This was not ideal
and required to re-generate them when changing protos.
The removal of generated classes is done in a separate CL
(aosp/1106801).

Bug: 132880619
Change-Id: I63a162c4bd2f7f8c9afcf843cf574fa8a6805805
diff --git a/src/protozero/protoc_plugin/BUILD.gn b/src/protozero/protoc_plugin/BUILD.gn
index 432abd8..ab8f9a7 100644
--- a/src/protozero/protoc_plugin/BUILD.gn
+++ b/src/protozero/protoc_plugin/BUILD.gn
@@ -14,6 +14,8 @@
 
 import("../../../gn/perfetto_host_executable.gni")
 
+# The plugin that generates zero-copy serializers and deserializers. Those are
+# the xxx.pbzero.h headers used all over the codebase.
 perfetto_host_executable("protozero_plugin") {
   sources = [
     "protozero_plugin.cc",
@@ -24,3 +26,17 @@
     "../../../src/base",
   ]
 }
+
+# The plugin that generates standalone C++ objects from protos (xxx.gen.h).
+# This is used for core classes traced needs to know about such as
+# DataSourceDescriptor.
+perfetto_host_executable("cpp_obj_plugin") {
+  sources = [
+    "cpp_obj_plugin.cc",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../gn:protoc_lib",
+    "../../../src/base",
+  ]
+}
diff --git a/src/protozero/protoc_plugin/cpp_obj_plugin.cc b/src/protozero/protoc_plugin/cpp_obj_plugin.cc
new file mode 100644
index 0000000..0e6f287
--- /dev/null
+++ b/src/protozero/protoc_plugin/cpp_obj_plugin.cc
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <stack>
+#include <vector>
+
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/compiler/importer.h>
+#include <google/protobuf/compiler/plugin.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/util/field_comparator.h>
+#include <google/protobuf/util/message_differencer.h>
+
+#include "perfetto/ext/base/string_utils.h"
+
+namespace protozero {
+namespace {
+
+using namespace google::protobuf;
+using namespace google::protobuf::compiler;
+using namespace google::protobuf::io;
+using perfetto::base::SplitString;
+using perfetto::base::StripChars;
+using perfetto::base::StripSuffix;
+using perfetto::base::ToUpper;
+
+static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE;
+
+static const char kHeader[] =
+    "// DO NOT EDIT. Autogenerated by Perfetto cpp_obj_plugin\n";
+
+std::string GetProtoHeader(const FileDescriptor* file) {
+  return StripSuffix(file->name(), ".proto") + ".pb.h";
+}
+
+template <typename T = Descriptor>
+std::string GetFullName(const T* msg, bool with_namespace = false) {
+  std::string full_type;
+  full_type.append(msg->name());
+  for (const Descriptor* par = msg->containing_type(); par;
+       par = par->containing_type()) {
+    full_type.insert(0, par->name() + "_");
+  }
+  if (with_namespace) {
+    std::vector<std::string> namespaces =
+        SplitString(msg->file()->package(), ".");
+    for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++) {
+      full_type.insert(0, *it + "::");
+    }
+  }
+  return full_type;
+}
+
+class CppObjGenerator : public ::google::protobuf::compiler::CodeGenerator {
+ public:
+  CppObjGenerator();
+  ~CppObjGenerator() override;
+
+  // CodeGenerator implementation
+  bool Generate(const google::protobuf::FileDescriptor* file,
+                const std::string& options,
+                GeneratorContext* context,
+                std::string* error) const override;
+
+ private:
+  std::string GetCppType(const FieldDescriptor* field, bool constref) const;
+  void GenEnum(const EnumDescriptor*, Printer*) const;
+  void GenEnumAliases(const EnumDescriptor*, Printer*) const;
+  void GenClassDecl(const Descriptor*, Printer*) const;
+  void GenClassDef(const Descriptor*, Printer*) const;
+};
+
+CppObjGenerator::CppObjGenerator() = default;
+CppObjGenerator::~CppObjGenerator() = default;
+
+bool CppObjGenerator::Generate(const google::protobuf::FileDescriptor* file,
+                               const std::string& /*options*/,
+                               GeneratorContext* context,
+                               std::string* error) const {
+  auto get_file_name = [](const FileDescriptor* proto) {
+    return StripSuffix(proto->name(), ".proto") + ".gen";
+  };
+
+  const std::unique_ptr<ZeroCopyOutputStream> h_fstream(
+      context->Open(get_file_name(file) + ".h"));
+  const std::unique_ptr<ZeroCopyOutputStream> cc_fstream(
+      context->Open(get_file_name(file) + ".cc"));
+
+  // Variables are delimited by $.
+  Printer h_printer(h_fstream.get(), '$');
+  Printer cc_printer(cc_fstream.get(), '$');
+
+  std::string include_guard = file->package() + "_" + file->name() + "_CPP_H_";
+  include_guard = ToUpper(include_guard);
+  include_guard = StripChars(include_guard, ".-/\\", '_');
+
+  h_printer.Print(kHeader);
+  h_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
+  h_printer.Print("#include <stdint.h>\n");
+  h_printer.Print("#include <vector>\n");
+  h_printer.Print("#include <string>\n");
+  h_printer.Print("#include <type_traits>\n\n");
+  h_printer.Print("#include \"perfetto/base/copyable_ptr.h\"\n");
+  h_printer.Print("#include \"perfetto/base/export.h\"\n\n");
+
+  cc_printer.Print(kHeader);
+  cc_printer.Print("#pragma GCC diagnostic push\n");
+  cc_printer.Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n");
+
+  // Generate includes for translated types of dependencies.
+
+  // Figure out the subset of imports that are used only for lazy fields. We
+  // won't emit a C++ #include for them. This code is overly aggressive at
+  // removing imports: it rules them out as soon as it sees one lazy field
+  // whose type is defined in that import. A 100% correct solution would require
+  // to check that *all* dependent types for a given import are lazy before
+  // excluding that. In practice we don't need that because we don't use imports
+  // for both lazy and non-lazy fields.
+  std::set<std::string> lazy_imports;
+  for (int m = 0; m < file->message_type_count(); m++) {
+    const Descriptor* msg = file->message_type(m);
+    for (int i = 0; i < msg->field_count(); i++) {
+      const FieldDescriptor* field = msg->field(i);
+      if (field->options().lazy()) {
+        lazy_imports.insert(field->message_type()->file()->name());
+      }
+    }
+  }
+
+  // Include the .pb.h for the current file.
+  cc_printer.Print("\n#include \"$f$\"\n", "f", GetProtoHeader(file));
+
+  // Recursively traverse all imports and turn them into #include(s).
+  std::vector<const FileDescriptor*> imports_to_visit;
+  std::set<const FileDescriptor*> imports_visited;
+  imports_to_visit.push_back(file);
+
+  while (!imports_to_visit.empty()) {
+    const FileDescriptor* cur = imports_to_visit.back();
+    imports_to_visit.pop_back();
+    imports_visited.insert(cur);
+    cc_printer.Print("#include \"$f$.h\"\n", "f", get_file_name(cur));
+    for (int i = 0; i < cur->dependency_count(); i++) {
+      const FileDescriptor* dep = cur->dependency(i);
+      if (imports_visited.count(dep) || lazy_imports.count(dep->name()))
+        continue;
+      imports_to_visit.push_back(dep);
+    }
+  }
+
+  // Compute all nested types to generate forward declarations later.
+
+  std::set<const Descriptor*> local_types;  // Cur .proto file only.
+  std::set<const Descriptor*> all_types;    // All deps
+  std::set<const EnumDescriptor*> local_enums;
+  std::set<const EnumDescriptor*> all_enums;
+
+  auto add_enum = [&local_enums, &all_enums,
+                   &file](const EnumDescriptor* enum_desc) {
+    if (all_enums.count(enum_desc))
+      return;
+    all_enums.insert(enum_desc);
+    if (enum_desc->file() == file)
+      local_enums.insert(enum_desc);
+  };
+
+  std::stack<const Descriptor*> recursion_stack;
+  for (int i = 0; i < file->message_type_count(); i++)
+    recursion_stack.push(file->message_type(i));
+
+  while (!recursion_stack.empty()) {
+    const Descriptor* msg = recursion_stack.top();
+    recursion_stack.pop();
+    if (all_types.count(msg))
+      continue;
+    all_types.insert(msg);
+    if (msg->file() == file)
+      local_types.insert(msg);
+
+    for (int i = 0; i < msg->nested_type_count(); i++)
+      recursion_stack.push(msg->nested_type(i));
+
+    for (int i = 0; i < msg->enum_type_count(); i++)
+      add_enum(msg->enum_type(i));
+
+    for (int i = 0; i < msg->field_count(); i++) {
+      const FieldDescriptor* field = msg->field(i);
+      if (field->has_default_value()) {
+        *error = "field " + field->name() +
+                 ": Explicitly declared default values are not supported";
+        return false;
+      }
+      if (field->options().lazy() &&
+          (field->is_repeated() || field->type() != TYPE_MESSAGE)) {
+        *error = "[lazy=true] is supported only on non-repeated fields\n";
+        return false;
+      }
+
+      if (field->type() == TYPE_MESSAGE && !field->options().lazy())
+        recursion_stack.push(field->message_type());
+
+      if (field->type() == FieldDescriptor::TYPE_ENUM)
+        add_enum(field->enum_type());
+    }
+  }  //  while (!recursion_stack.empty())
+
+  // Generate forward declarations in the header for proto types.
+  h_printer.Print("// Forward declarations for protobuf types.\n");
+  std::vector<std::string> namespaces = SplitString(file->package(), ".");
+  for (size_t i = 0; i < namespaces.size(); i++)
+    h_printer.Print("namespace $n$ {\n", "n", namespaces[i]);
+
+  for (const Descriptor* msg : all_types)
+    h_printer.Print("class $n$;\n", "n", GetFullName(msg));
+
+  for (size_t i = 0; i < namespaces.size(); i++)
+    h_printer.Print("}\n");
+
+  h_printer.Print("\nnamespace perfetto {\n");
+  cc_printer.Print("\nnamespace perfetto {\n");
+
+  // Generate fwd declarations for C++ types.
+  for (const EnumDescriptor* enm : all_enums) {
+    h_printer.Print("enum $n$ : int;\n", "n", GetFullName(enm));
+  }
+
+  for (const Descriptor* msg : all_types)
+    h_printer.Print("class $n$;\n", "n", GetFullName(msg));
+
+  // Generate declarations and definitions.
+  for (const EnumDescriptor* enm : local_enums)
+    GenEnum(enm, &h_printer);
+
+  for (const Descriptor* msg : local_types) {
+    GenClassDecl(msg, &h_printer);
+    GenClassDef(msg, &cc_printer);
+  }
+
+  cc_printer.Print("}  // namespace perfetto\n");
+  cc_printer.Print("#pragma GCC diagnostic pop\n");
+
+  h_printer.Print("}  // namespace perfetto\n");
+  h_printer.Print("\n#endif  // $g$\n", "g", include_guard);
+
+  return true;
+}
+
+std::string CppObjGenerator::GetCppType(const FieldDescriptor* field,
+                                        bool constref) const {
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_DOUBLE:
+      return "double";
+    case FieldDescriptor::TYPE_FLOAT:
+      return "float";
+    case FieldDescriptor::TYPE_FIXED32:
+    case FieldDescriptor::TYPE_UINT32:
+      return "uint32_t";
+    case FieldDescriptor::TYPE_SFIXED32:
+    case FieldDescriptor::TYPE_INT32:
+    case FieldDescriptor::TYPE_SINT32:
+      return "int32_t";
+    case FieldDescriptor::TYPE_FIXED64:
+    case FieldDescriptor::TYPE_UINT64:
+      return "uint64_t";
+    case FieldDescriptor::TYPE_SFIXED64:
+    case FieldDescriptor::TYPE_SINT64:
+    case FieldDescriptor::TYPE_INT64:
+      return "int64_t";
+    case FieldDescriptor::TYPE_BOOL:
+      return "bool";
+    case FieldDescriptor::TYPE_STRING:
+    case FieldDescriptor::TYPE_BYTES:
+      return constref ? "const std::string&" : "std::string";
+    case FieldDescriptor::TYPE_MESSAGE:
+      assert(!field->options().lazy());
+      return constref ? "const " + GetFullName(field->message_type()) + "&"
+                      : GetFullName(field->message_type());
+    case FieldDescriptor::TYPE_ENUM:
+      return GetFullName(field->enum_type());
+    case FieldDescriptor::TYPE_GROUP:
+      abort();
+  }
+  abort();  // for gcc
+}
+
+void CppObjGenerator::GenEnum(const EnumDescriptor* enum_desc,
+                              Printer* p) const {
+  std::string full_name = GetFullName(enum_desc);
+  p->Print("enum $f$ : int {\n", "f", full_name);
+  for (int e = 0; e < enum_desc->value_count(); e++) {
+    const EnumValueDescriptor* value = enum_desc->value(e);
+    p->Print("  $f$_$n$ = $v$,\n", "f", full_name, "n", value->name(), "v",
+             std::to_string(value->number()));
+  }
+  p->Print("};\n");
+}
+
+void CppObjGenerator::GenEnumAliases(const EnumDescriptor* enum_desc,
+                                     Printer* p) const {
+  std::string full_name = GetFullName(enum_desc);
+  for (int e = 0; e < enum_desc->value_count(); e++) {
+    const EnumValueDescriptor* value = enum_desc->value(e);
+    p->Print("static constexpr auto $n$ = $f$_$n$;\n", "f", full_name, "n",
+             value->name());
+  }
+}
+
+void CppObjGenerator::GenClassDecl(const Descriptor* msg, Printer* p) const {
+  std::string full_name = GetFullName(msg);
+  p->Print("\nclass PERFETTO_EXPORT $n$ {\n", "n", full_name);
+  p->Print(" public:\n");
+  p->Indent();
+
+  // Do a first pass to generate aliases for nested types.
+  // e.g., using Foo = Parent_Foo;
+  for (int i = 0; i < msg->nested_type_count(); i++) {
+    const Descriptor* nested_msg = msg->nested_type(i);
+    p->Print("using $n$ = $f$;\n", "n", nested_msg->name(), "f",
+             GetFullName(nested_msg));
+  }
+  for (int i = 0; i < msg->enum_type_count(); i++) {
+    const EnumDescriptor* nested_enum = msg->enum_type(i);
+    p->Print("using $n$ = $f$;\n", "n", nested_enum->name(), "f",
+             GetFullName(nested_enum));
+    GenEnumAliases(nested_enum, p);
+  }
+
+  p->Print("$n$();\n", "n", full_name);
+  p->Print("~$n$();\n", "n", full_name);
+  p->Print("$n$($n$&&) noexcept;\n", "n", full_name);
+  p->Print("$n$& operator=($n$&&);\n", "n", full_name);
+  p->Print("$n$(const $n$&);\n", "n", full_name);
+  p->Print("$n$& operator=(const $n$&);\n", "n", full_name);
+  p->Print("bool operator==(const $n$&) const;\n", "n", full_name);
+  p->Print(
+      "bool operator!=(const $n$& other) const { return !(*this == other); }\n",
+      "n", full_name);
+  p->Print("\n");
+
+  std::string proto_type = GetFullName(msg, true);
+  p->Print("// Raw proto decoding.\n");
+  p->Print("void ParseRawProto(const std::string&);\n");
+  p->Print("// Conversion methods from/to the corresponding protobuf types.\n");
+  p->Print("void FromProto(const $p$&);\n", "p", proto_type);
+  p->Print("void ToProto($p$*) const;\n", "p", proto_type);
+
+  // Generate accessors.
+  for (int i = 0; i < msg->field_count(); i++) {
+    const FieldDescriptor* field = msg->field(i);
+    p->Print("\n");
+    if (field->options().lazy()) {
+      p->Print("const std::string& $n$_raw() const { return $n$_; }\n", "n",
+               field->lowercase_name());
+      p->Print("void set_$n$_raw(const std::string& raw) { $n$_ = raw; }\n",
+               "n", field->lowercase_name());
+    } else if (!field->is_repeated()) {
+      if (field->type() == TYPE_MESSAGE) {
+        p->Print("$t$ $n$() const { return *$n$_; }\n", "t",
+                 GetCppType(field, true), "n", field->lowercase_name());
+        p->Print("$t$* mutable_$n$() { return $n$_.get(); }\n", "t",
+                 GetCppType(field, false), "n", field->lowercase_name());
+      } else {
+        p->Print("$t$ $n$() const { return $n$_; }\n", "t",
+                 GetCppType(field, true), "n", field->lowercase_name());
+        p->Print("void set_$n$($t$ value) { $n$_ = value; }\n", "t",
+                 GetCppType(field, true), "n", field->lowercase_name());
+        if (field->type() == FieldDescriptor::TYPE_BYTES) {
+          p->Print(
+              "void set_$n$(const void* p, size_t s) { "
+              "$n$_.assign(reinterpret_cast<const char*>(p), s); }\n",
+              "n", field->lowercase_name());
+        }
+      }
+    } else {  // is_repeated()
+      p->Print(
+          "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
+          "t", GetCppType(field, false), "n", field->lowercase_name());
+      p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
+               GetCppType(field, false), "n", field->lowercase_name());
+      p->Print("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t",
+               GetCppType(field, false), "n", field->lowercase_name());
+      p->Print("void clear_$n$() { $n$_.clear(); }\n", "n",
+               field->lowercase_name());
+
+      if (field->type() == TYPE_MESSAGE && !field->options().lazy()) {
+        p->Print(
+            "$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); "
+            "}\n",
+            "t", GetCppType(field, false), "n", field->lowercase_name());
+      } else {
+        p->Print(
+            "$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
+            "t", GetCppType(field, false), "n", field->lowercase_name());
+      }
+    }
+  }
+  p->Outdent();
+  p->Print("\n private:\n");
+  p->Indent();
+
+  // Generate fields.
+  for (int i = 0; i < msg->field_count(); i++) {
+    const FieldDescriptor* field = msg->field(i);
+    if (field->options().lazy()) {
+      p->Print("std::string $n$_;  // [lazy=true]\n", "n",
+               field->lowercase_name());
+    } else if (!field->is_repeated()) {
+      std::string type = GetCppType(field, false);
+      if (field->type() == TYPE_MESSAGE) {
+        type = "::perfetto::base::CopyablePtr<" + type + ">";
+        p->Print("$t$ $n$_;\n", "t", type, "n", field->lowercase_name());
+      } else {
+        p->Print("$t$ $n$_{};\n", "t", type, "n", field->lowercase_name());
+      }
+    } else {  // is_repeated()
+      p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
+               field->lowercase_name());
+    }
+  }
+  p->Print("\n");
+  p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
+  p->Print("// with future versions of .proto files.\n");
+  p->Print("std::string unknown_fields_;\n");
+  p->Outdent();
+  p->Print("};\n\n");
+}
+
+void CppObjGenerator::GenClassDef(const Descriptor* msg, Printer* p) const {
+  p->Print("\n");
+  std::string full_name = GetFullName(msg);
+
+  p->Print("$n$::$n$() = default;\n", "n", full_name);
+  p->Print("$n$::~$n$() = default;\n", "n", full_name);
+  p->Print("$n$::$n$(const $n$&) = default;\n", "n", full_name);
+  p->Print("$n$& $n$::operator=(const $n$&) = default;\n", "n", full_name);
+  p->Print("$n$::$n$($n$&&) noexcept = default;\n", "n", full_name);
+  p->Print("$n$& $n$::operator=($n$&&) = default;\n", "n", full_name);
+
+  p->Print("\n");
+
+  // Comparison operator
+  p->Print("bool $n$::operator==(const $n$& other) const {\n", "n", full_name);
+  p->Indent();
+
+  p->Print("return unknown_fields_ == other.unknown_fields_");
+  for (int i = 0; i < msg->field_count(); i++)
+    p->Print("\n && $n$_ == other.$n$_", "n", msg->field(i)->lowercase_name());
+  p->Print(";");
+  p->Outdent();
+  p->Print("\n}\n\n");
+
+  std::string proto_type = GetFullName(msg, true);
+
+  // Genrate the ParseRawProto() method definition.
+  p->Print("void $f$::ParseRawProto(const std::string& raw) {\n", "f",
+           full_name);
+  p->Indent();
+  p->Print("$p$ proto;\n", "p", proto_type);
+  p->Print("proto.ParseFromString(raw);\n");
+  p->Print("FromProto(proto);\n");
+  p->Outdent();
+  p->Print("}\n\n");
+
+  // Genrate the FromProto() method definition.
+  p->Print("void $f$::FromProto(const $p$& proto) {\n", "f", full_name, "p",
+           proto_type);
+  p->Indent();
+  for (int i = 0; i < msg->field_count(); i++) {
+    p->Print("\n");
+    const FieldDescriptor* field = msg->field(i);
+    if (field->options().lazy()) {
+      p->Print("$n$_ = proto.$n$().SerializeAsString();\n", "n",
+               field->lowercase_name());
+    } else if (!field->is_repeated()) {
+      if (field->type() == TYPE_MESSAGE) {
+        p->Print("$n$_->FromProto(proto.$n$());\n", "n",
+                 field->lowercase_name());
+      } else {
+        p->Print(
+            "static_assert(sizeof($n$_) == sizeof(proto.$n$()), \"size "
+            "mismatch\");\n",
+            "n", field->lowercase_name());
+        p->Print("$n$_ = static_cast<decltype($n$_)>(proto.$n$());\n", "n",
+                 field->lowercase_name());
+      }
+    } else {  // is_repeated()
+      p->Print("$n$_.clear();\n", "n", field->lowercase_name());
+      p->Print("for (const auto& field : proto.$n$()) {\n", "n",
+               field->lowercase_name());
+      p->Print("  $n$_.emplace_back();\n", "n", field->lowercase_name());
+      if (field->type() == TYPE_MESSAGE) {
+        p->Print("  $n$_.back().FromProto(field);\n", "n",
+                 field->lowercase_name());
+      } else {
+        p->Print(
+            "static_assert(sizeof($n$_.back()) == sizeof(proto.$n$(0)), \"size "
+            "mismatch\");\n",
+            "n", field->lowercase_name());
+        p->Print(
+            "  $n$_.back() = static_cast<decltype($n$_)::value_type>(field);\n",
+            "n", field->lowercase_name());
+      }
+      p->Print("}\n");
+    }
+  }
+  p->Print("unknown_fields_ = proto.unknown_fields();\n");
+  p->Outdent();
+  p->Print("}\n\n");
+
+  // Genrate the ToProto() method definition.
+  p->Print("void $f$::ToProto($p$* proto) const {\n", "f", full_name, "p",
+           proto_type);
+  p->Indent();
+  p->Print("proto->Clear();\n");
+  for (int i = 0; i < msg->field_count(); i++) {
+    p->Print("\n");
+    const FieldDescriptor* field = msg->field(i);
+    if (field->options().lazy()) {
+      p->Print("proto->mutable_$n$()->ParseFromString($n$_);\n", "n",
+               field->lowercase_name());
+    } else if (!field->is_repeated()) {
+      if (field->type() == TYPE_MESSAGE) {
+        p->Print("$n$_->ToProto(proto->mutable_$n$());\n", "n",
+                 field->lowercase_name());
+      } else {
+        p->Print(
+            "static_assert(sizeof($n$_) == sizeof(proto->$n$()), \"size "
+            "mismatch\");\n",
+            "n", field->lowercase_name());
+        p->Print("proto->set_$n$(static_cast<decltype(proto->$n$())>($n$_));\n",
+                 "n", field->lowercase_name());
+      }
+    } else {  // is_repeated()
+      p->Print("for (const auto& it : $n$_) {\n", "n", field->lowercase_name());
+      if (field->type() == TYPE_MESSAGE) {
+        p->Print("  auto* entry = proto->add_$n$();\n", "n",
+                 field->lowercase_name());
+        p->Print("  it.ToProto(entry);\n");
+      } else {
+        p->Print(
+            "  proto->add_$n$(static_cast<decltype(proto->$n$(0))>(it));\n",
+            "n", field->lowercase_name());
+        p->Print(
+            "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
+            "mismatch\");\n",
+            "n", field->lowercase_name());
+      }
+      p->Print("}\n");
+    }
+  }
+  p->Print("*(proto->mutable_unknown_fields()) = unknown_fields_;\n");
+  p->Outdent();
+  p->Print("}\n\n");
+}
+
+}  // namespace
+}  // namespace protozero
+
+int main(int argc, char** argv) {
+  ::protozero::CppObjGenerator generator;
+  return google::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/tools/BUILD.gn b/tools/BUILD.gn
index 19313b5..8ee64e9 100644
--- a/tools/BUILD.gn
+++ b/tools/BUILD.gn
@@ -25,7 +25,6 @@
     ":idle_alloc",
     "compact_reencode",
     "ftrace_proto_gen",
-    "proto_to_cpp",
     "protoprofile",
     "trace_to_text",
     "trace_to_text:trace_to_text_lite",
diff --git a/tools/gen_all b/tools/gen_all
index 094bd88..92d545c 100755
--- a/tools/gen_all
+++ b/tools/gen_all
@@ -56,11 +56,6 @@
     call('ninja', '-C', out, 'protoc')
     call('gen_binary_descriptors', '--protoc', protoc_path(out), *check_only)
 
-    # TODO(primiano): gen_tracing_cpp_headers_from_protos is going away soon
-    # and there is no point adding --check-only support to that.
-    if not check_only:
-      call('gen_tracing_cpp_headers_from_protos', out)
-
   except AssertionError as e:
     if not str(e):
       raise
diff --git a/tools/gen_tracing_cpp_headers_from_protos b/tools/gen_tracing_cpp_headers_from_protos
deleted file mode 100755
index c214698..0000000
--- a/tools/gen_tracing_cpp_headers_from_protos
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2017 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.
-
-import os
-import subprocess
-import sys
-
-ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-PUB_CORE_H = 'include/perfetto/tracing/core'
-CORE_H = 'include/perfetto/ext/tracing/core'
-CORE_CPP = 'src/tracing/core'
-
-PROTOS = (
-    # Classes that are exposed as part of the public API surface.
-    ('protos/perfetto/common/data_source_descriptor.proto', PUB_CORE_H, CORE_CPP
-    ),
-    ('protos/perfetto/common/tracing_service_state.proto', PUB_CORE_H,
-     CORE_CPP),
-    ('protos/perfetto/config/chrome/chrome_config.proto', PUB_CORE_H, CORE_CPP),
-    ('protos/perfetto/config/data_source_config.proto', PUB_CORE_H, CORE_CPP),
-    ('protos/perfetto/config/test_config.proto', PUB_CORE_H, CORE_CPP),
-    ('protos/perfetto/config/trace_config.proto', PUB_CORE_H, CORE_CPP),
-
-    # Classes that are exposes only in the /ext/ (unstable) API surface.
-    ('protos/perfetto/common/commit_data_request.proto', CORE_H, CORE_CPP),
-    ('protos/perfetto/common/observable_events.proto', CORE_H, CORE_CPP),
-    ('protos/perfetto/common/trace_stats.proto', CORE_H, CORE_CPP),
-
-    # Generate ftrace cpp/h files into the src/traced/probes/ directory.
-    ('protos/perfetto/config/ftrace/ftrace_config.proto',
-     'src/traced/probes/ftrace', 'src/traced/probes/ftrace'),
-
-    # Generate profiling cpp/h files into the src/profiling/memory/ directory.
-    ('protos/perfetto/config/profiling/heapprofd_config.proto',
-     'src/profiling/memory', 'src/profiling/memory'),
-    ('protos/perfetto/config/profiling/java_hprof_config.proto',
-     'src/profiling/memory', 'src/profiling/memory'),
-)
-
-
-def run(cmd):
-  print('\nRunning ' + ' '.join(cmd))
-  subprocess.check_call(cmd)
-
-
-def main():
-  if not os.path.exists('.gn'):
-    print('This script must be executed from the perfetto root directory')
-    return 1
-  if len(sys.argv) < 2:
-    print('Usage: %s out/xxx' % sys.argv[0])
-    return 1
-  out_dir = sys.argv[1]
-  arch = 'mac' if sys.platform == 'darwin' else 'linux64'
-  clang_format_path = os.path.join(ROOT_DIR, 'buildtools', arch, 'clang-format')
-  clang_format = [clang_format_path, '-i', '--sort-includes']
-  ninja = os.path.join(ROOT_DIR, 'tools', 'ninja')
-  run([ninja, '-C', out_dir, 'proto_to_cpp'])
-  tool = os.path.join(out_dir, 'proto_to_cpp')
-  assert (os.path.exists(tool))
-  for args in PROTOS:
-    proto, header_dir, cpp_dir = args
-    include_dir = header_dir.replace('include/', '')
-    run([tool, proto] + [header_dir, cpp_dir, include_dir])
-    fname = os.path.basename(proto).replace('.proto', '')
-    run(clang_format + [os.path.join(header_dir, fname + '.h')])
-    run(clang_format + [os.path.join(cpp_dir, fname + '.cc')])
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/tools/proto_to_cpp/BUILD.gn b/tools/proto_to_cpp/BUILD.gn
deleted file mode 100644
index aaa7c7e..0000000
--- a/tools/proto_to_cpp/BUILD.gn
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2017 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.
-
-import("../../gn/perfetto_host_executable.gni")
-
-perfetto_host_executable("proto_to_cpp") {
-  testonly = true
-  deps = [
-    "../../gn:default_deps",
-    "../../gn:protobuf_full",
-    "../../src/base",
-  ]
-  sources = [
-    "proto_to_cpp.cc",
-  ]
-}
diff --git a/tools/proto_to_cpp/proto_to_cpp.cc b/tools/proto_to_cpp/proto_to_cpp.cc
deleted file mode 100644
index 5e995f2..0000000
--- a/tools/proto_to_cpp/proto_to_cpp.cc
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/util/field_comparator.h>
-#include <google/protobuf/util/message_differencer.h>
-
-#include <stdio.h>
-
-#include <fstream>
-#include <iostream>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/string_utils.h"
-
-using namespace google::protobuf;
-using namespace google::protobuf::compiler;
-using namespace google::protobuf::io;
-using perfetto::base::SplitString;
-using perfetto::base::StripChars;
-using perfetto::base::StripSuffix;
-using perfetto::base::ToUpper;
-
-static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE;
-
-namespace {
-
-static const char kHeader[] = R"(/*
- * Copyright (C) 2017 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.
- */
-
-/*******************************************************************************
- * AUTOGENERATED - DO NOT EDIT
- *******************************************************************************
- * This file has been generated from the protobuf message
- * $p$
- * by
- * $f$.
- * If you need to make changes here, change the .proto file and then run
- * ./tools/gen_tracing_cpp_headers_from_protos
- */
-
-)";
-
-class ErrorPrinter : public MultiFileErrorCollector {
-  virtual void AddError(const string& filename,
-                        int line,
-                        int col,
-                        const string& msg) {
-    PERFETTO_ELOG("%s %d:%d: %s", filename.c_str(), line, col, msg.c_str());
-  }
-  virtual void AddWarning(const string& filename,
-                          int line,
-                          int col,
-                          const string& msg) {
-    PERFETTO_ILOG("%s %d:%d: %s", filename.c_str(), line, col, msg.c_str());
-  }
-};
-
-std::string GetProtoHeader(const FileDescriptor* proto_file) {
-  return StripSuffix(proto_file->name(), ".proto") + ".pb.h";
-}
-
-std::string GetFwdDeclType(const Descriptor* msg, bool with_namespace = false) {
-  std::string full_type;
-  full_type.append(msg->name());
-  for (const Descriptor* par = msg->containing_type(); par;
-       par = par->containing_type()) {
-    full_type.insert(0, par->name() + "_");
-  }
-  if (with_namespace) {
-    std::vector<std::string> namespaces =
-        SplitString(msg->file()->package(), ".");
-    for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++) {
-      full_type.insert(0, *it + "::");
-    }
-  }
-  return full_type;
-}
-
-void GenFwdDecl(const Descriptor* msg, Printer* p, bool root_cpp_only = false) {
-  if (!root_cpp_only) {
-    p->Print("class $n$;", "n", GetFwdDeclType(msg));
-  } else if (msg->full_name() == msg->file()->package() + "." + msg->name()) {
-    p->Print("class $n$;", "n", msg->name());
-  }
-
-  // Recurse into subtypes
-  for (int i = 0; i < msg->field_count(); i++) {
-    const FieldDescriptor* field = msg->field(i);
-    if (field->type() == TYPE_MESSAGE && !field->options().lazy()) {
-      GenFwdDecl(field->message_type(), p, root_cpp_only);
-    }
-  }
-}
-
-}  // namespace
-
-class ProtoToCpp {
- public:
-  ProtoToCpp(const std::string& header_dir,
-             const std::string& cpp_dir,
-             const std::string& include_path);
-
-  static std::string GetCppType(const FieldDescriptor* field, bool constref);
-
-  void Convert(const std::string& src_proto);
-  std::string GetHeaderPath(const FileDescriptor*);
-  std::string GetCppPath(const FileDescriptor*);
-  std::string GetIncludePath(const FileDescriptor*);
-  void GenHeader(const Descriptor*, Printer*);
-  void GenCpp(const Descriptor*, Printer*, std::string prefix);
-
- private:
-  std::string header_dir_;
-  std::string cpp_dir_;
-  std::string include_path_;
-  DiskSourceTree dst_;
-  ErrorPrinter error_printer_;
-  Importer importer_;
-};
-
-ProtoToCpp::ProtoToCpp(const std::string& header_dir,
-                       const std::string& cpp_dir,
-                       const std::string& include_path)
-    : header_dir_(header_dir),
-      cpp_dir_(cpp_dir),
-      include_path_(include_path),
-      importer_(&dst_, &error_printer_) {
-  dst_.MapPath("", "");  // Yes, this tautology is needed :/.
-}
-
-std::string ProtoToCpp::GetHeaderPath(const FileDescriptor* proto_file) {
-  std::string basename = SplitString(proto_file->name(), "/").back();
-  return header_dir_ + "/" + StripSuffix(basename, ".proto") + ".h";
-}
-
-std::string ProtoToCpp::GetCppPath(const FileDescriptor* proto_file) {
-  std::string basename = SplitString(proto_file->name(), "/").back();
-  return cpp_dir_ + "/" + StripSuffix(basename, ".proto") + ".cc";
-}
-
-std::string ProtoToCpp::GetIncludePath(const FileDescriptor* proto_file) {
-  std::string basename = SplitString(proto_file->name(), "/").back();
-  return include_path_ + "/" + StripSuffix(basename, ".proto") + ".h";
-}
-
-std::string ProtoToCpp::GetCppType(const FieldDescriptor* field,
-                                   bool constref) {
-  switch (field->type()) {
-    case FieldDescriptor::TYPE_DOUBLE:
-      return "double";
-    case FieldDescriptor::TYPE_FLOAT:
-      return "float";
-    case FieldDescriptor::TYPE_FIXED32:
-    case FieldDescriptor::TYPE_UINT32:
-      return "uint32_t";
-    case FieldDescriptor::TYPE_SFIXED32:
-    case FieldDescriptor::TYPE_INT32:
-    case FieldDescriptor::TYPE_SINT32:
-      return "int32_t";
-    case FieldDescriptor::TYPE_FIXED64:
-    case FieldDescriptor::TYPE_UINT64:
-      return "uint64_t";
-    case FieldDescriptor::TYPE_SFIXED64:
-    case FieldDescriptor::TYPE_SINT64:
-    case FieldDescriptor::TYPE_INT64:
-      return "int64_t";
-    case FieldDescriptor::TYPE_BOOL:
-      return "bool";
-    case FieldDescriptor::TYPE_STRING:
-    case FieldDescriptor::TYPE_BYTES:
-      return constref ? "const std::string&" : "std::string";
-    case FieldDescriptor::TYPE_MESSAGE:
-      PERFETTO_CHECK(!field->options().lazy());
-      return constref ? "const " + field->message_type()->name() + "&"
-                      : field->message_type()->name();
-    case FieldDescriptor::TYPE_ENUM:
-      return field->enum_type()->name();
-    case FieldDescriptor::TYPE_GROUP:
-      PERFETTO_FATAL("No cpp type for a group field.");
-  }
-  PERFETTO_FATAL("Not reached");  // for gcc
-}
-
-void ProtoToCpp::Convert(const std::string& src_proto) {
-  const FileDescriptor* proto_file = importer_.Import(src_proto);
-  if (!proto_file) {
-    PERFETTO_ELOG("Failed to load %s", src_proto.c_str());
-    exit(1);
-  }
-
-  std::string dst_header = GetHeaderPath(proto_file);
-  std::string dst_cpp = GetCppPath(proto_file);
-
-  std::ofstream header_ostr;
-  header_ostr.open(dst_header);
-  PERFETTO_CHECK(header_ostr.is_open());
-  OstreamOutputStream header_proto_ostr(&header_ostr);
-  Printer header_printer(&header_proto_ostr, '$');
-
-  std::ofstream cpp_ostr;
-  cpp_ostr.open(dst_cpp);
-  PERFETTO_CHECK(cpp_ostr.is_open());
-  OstreamOutputStream cpp_proto_ostr(&cpp_ostr);
-  Printer cpp_printer(&cpp_proto_ostr, '$');
-
-  std::string include_guard = dst_header + "_";
-  include_guard = ToUpper(include_guard);
-  include_guard = StripChars(include_guard, ".-/\\", '_');
-  header_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
-  header_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
-  header_printer.Print("#include <stdint.h>\n");
-  header_printer.Print("#include <vector>\n");
-  header_printer.Print("#include <string>\n");
-  header_printer.Print("#include <type_traits>\n\n");
-  header_printer.Print("#include \"perfetto/base/copyable_ptr.h\"\n");
-  header_printer.Print("#include \"perfetto/base/export.h\"\n\n");
-
-  cpp_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
-  if (dst_header.find("include/") == 0) {
-    cpp_printer.Print("#include \"$f$\"\n", "f",
-                      dst_header.substr(strlen("include/")));
-  } else {
-    cpp_printer.Print("#include \"$f$\"\n", "f", dst_header);
-  }
-
-  // Generate includes for translated types of dependencies.
-
-  // Figure out the subset of imports that are used only for lazy fields. We
-  // won't emit a C++ #include for them. This code is overly aggressive at
-  // removing imports: it rules them out as soon as it sees one lazy field
-  // whose type is defined in that import. A 100% correct solution would require
-  // to check that *all* dependent types for a given import are lazy before
-  // excluding that. In practice we don't need that because we don't use imports
-  // for both lazy and non-lazy fields.
-  std::set<std::string> lazy_imports;
-  for (int m = 0; m < proto_file->message_type_count(); m++) {
-    const Descriptor* msg = proto_file->message_type(m);
-    for (int i = 0; i < msg->field_count(); i++) {
-      const FieldDescriptor* field = msg->field(i);
-      if (field->options().lazy()) {
-        lazy_imports.insert(field->message_type()->file()->name());
-      }
-    }
-  }
-
-  cpp_printer.Print("\n#include \"$f$\"\n", "f", GetProtoHeader(proto_file));
-  for (int i = 0; i < proto_file->dependency_count(); i++) {
-    const FileDescriptor* dep = proto_file->dependency(i);
-    if (lazy_imports.count(dep->name()))
-      continue;
-    cpp_printer.Print("\n#include \"$f$\"\n", "f", GetIncludePath(dep));
-    cpp_printer.Print("#include \"$f$\"\n", "f", GetProtoHeader(dep));
-  }
-
-  // Generate forward declarations in the header for proto types.
-  header_printer.Print("// Forward declarations for protobuf types.\n");
-  std::vector<std::string> namespaces = SplitString(proto_file->package(), ".");
-  for (size_t i = 0; i < namespaces.size(); i++)
-    header_printer.Print("namespace $n$ {\n", "n", namespaces[i]);
-  for (int i = 0; i < proto_file->message_type_count(); i++)
-    GenFwdDecl(proto_file->message_type(i), &header_printer);
-  for (size_t i = 0; i < namespaces.size(); i++)
-    header_printer.Print("}\n");
-
-  header_printer.Print("\nnamespace perfetto {\n");
-  cpp_printer.Print("\nnamespace perfetto {\n");
-
-  // Generate fwd declarations for top-level classes.
-  for (int i = 0; i < proto_file->message_type_count(); i++)
-    GenFwdDecl(proto_file->message_type(i), &header_printer, true);
-  header_printer.Print("\n");
-
-  for (int i = 0; i < proto_file->message_type_count(); i++) {
-    const Descriptor* msg = proto_file->message_type(i);
-    GenHeader(msg, &header_printer);
-    GenCpp(msg, &cpp_printer, "");
-  }
-
-  cpp_printer.Print("}  // namespace perfetto\n");
-  header_printer.Print("}  // namespace perfetto\n");
-  header_printer.Print("\n#endif  // $g$\n", "g", include_guard);
-}
-
-void ProtoToCpp::GenHeader(const Descriptor* msg, Printer* p) {
-  PERFETTO_ILOG("GEN %s %s", msg->name().c_str(), msg->file()->name().c_str());
-  p->Print("\nclass PERFETTO_EXPORT $n$ {\n", "n", msg->name());
-  p->Print(" public:\n");
-  p->Indent();
-  // Do a first pass to generate nested types.
-  for (int i = 0; i < msg->field_count(); i++) {
-    const FieldDescriptor* field = msg->field(i);
-    if (field->has_default_value()) {
-      PERFETTO_FATAL(
-          "Error on field %s: Explicitly declared default values are not "
-          "supported",
-          field->name().c_str());
-    }
-
-    if (field->type() == FieldDescriptor::TYPE_ENUM) {
-      const EnumDescriptor* enum_desc = field->enum_type();
-      p->Print("enum $n$ {\n", "n", enum_desc->name());
-      for (int e = 0; e < enum_desc->value_count(); e++) {
-        const EnumValueDescriptor* value = enum_desc->value(e);
-        p->Print("  $n$ = $v$,\n", "n", value->name(), "v",
-                 std::to_string(value->number()));
-      }
-      p->Print("};\n");
-    } else if (field->type() == TYPE_MESSAGE &&
-               field->message_type()->file() == msg->file()) {
-      GenHeader(field->message_type(), p);
-    }
-  }
-
-  p->Print("$n$();\n", "n", msg->name());
-  p->Print("~$n$();\n", "n", msg->name());
-  p->Print("$n$($n$&&) noexcept;\n", "n", msg->name());
-  p->Print("$n$& operator=($n$&&);\n", "n", msg->name());
-  p->Print("$n$(const $n$&);\n", "n", msg->name());
-  p->Print("$n$& operator=(const $n$&);\n", "n", msg->name());
-  p->Print("bool operator==(const $n$&) const;\n", "n", msg->name());
-  p->Print(
-      "bool operator!=(const $n$& other) const { return !(*this == other); }\n",
-      "n", msg->name());
-  p->Print("\n");
-
-  std::string proto_type = GetFwdDeclType(msg, true);
-  p->Print("// Raw proto decoding.\n");
-  p->Print("void ParseRawProto(const std::string&);\n");
-  p->Print("// Conversion methods from/to the corresponding protobuf types.\n");
-  p->Print("void FromProto(const $p$&);\n", "p", proto_type);
-  p->Print("void ToProto($p$*) const;\n", "p", proto_type);
-
-  // Generate accessors.
-  for (int i = 0; i < msg->field_count(); i++) {
-    const FieldDescriptor* field = msg->field(i);
-    p->Print("\n");
-    if (field->options().lazy()) {
-      if (field->is_repeated() || field->type() != TYPE_MESSAGE) {
-        PERFETTO_FATAL(
-            "[lazy=true] is supported only on non-repeated submessage fields");
-      }
-      p->Print("const std::string& $n$_raw() const { return $n$_; }\n", "n",
-               field->lowercase_name());
-      p->Print("void set_$n$_raw(const std::string& raw) { $n$_ = raw; }\n",
-               "n", field->lowercase_name());
-    } else if (!field->is_repeated()) {
-      if (field->type() == TYPE_MESSAGE) {
-        p->Print("$t$ $n$() const { return *$n$_; }\n", "t",
-                 GetCppType(field, true), "n", field->lowercase_name());
-        p->Print("$t$* mutable_$n$() { return $n$_.get(); }\n", "t",
-                 GetCppType(field, false), "n", field->lowercase_name());
-      } else {
-        p->Print("$t$ $n$() const { return $n$_; }\n", "t",
-                 GetCppType(field, true), "n", field->lowercase_name());
-        p->Print("void set_$n$($t$ value) { $n$_ = value; }\n", "t",
-                 GetCppType(field, true), "n", field->lowercase_name());
-        if (field->type() == FieldDescriptor::TYPE_BYTES) {
-          p->Print(
-              "void set_$n$(const void* p, size_t s) { "
-              "$n$_.assign(reinterpret_cast<const char*>(p), s); }\n",
-              "n", field->lowercase_name());
-        }
-      }
-    } else {  // is_repeated()
-      p->Print(
-          "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
-          "t", GetCppType(field, false), "n", field->lowercase_name());
-      p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
-               GetCppType(field, false), "n", field->lowercase_name());
-      p->Print("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t",
-               GetCppType(field, false), "n", field->lowercase_name());
-      p->Print("void clear_$n$() { $n$_.clear(); }\n", "n",
-               field->lowercase_name());
-
-      if (field->type() == TYPE_MESSAGE && !field->options().lazy()) {
-        p->Print(
-            "$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); "
-            "}\n",
-            "t", GetCppType(field, false), "n", field->lowercase_name());
-      } else {
-        p->Print(
-            "$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
-            "t", GetCppType(field, false), "n", field->lowercase_name());
-      }
-    }
-  }
-  p->Outdent();
-  p->Print("\n private:\n");
-  p->Indent();
-
-  // Generate fields.
-  for (int i = 0; i < msg->field_count(); i++) {
-    const FieldDescriptor* field = msg->field(i);
-    if (field->options().lazy()) {
-      p->Print("std::string $n$_;  // [lazy=true]\n", "n",
-               field->lowercase_name());
-    } else if (!field->is_repeated()) {
-      std::string type = GetCppType(field, false);
-      if (field->type() == TYPE_MESSAGE) {
-        type = "::perfetto::base::CopyablePtr<" + type + ">";
-        p->Print("$t$ $n$_;\n", "t", type, "n", field->lowercase_name());
-      } else {
-        p->Print("$t$ $n$_{};\n", "t", type, "n", field->lowercase_name());
-      }
-    } else {  // is_repeated()
-      p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
-               field->lowercase_name());
-    }
-  }
-  p->Print("\n");
-  p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
-  p->Print("// with future versions of .proto files.\n");
-  p->Print("std::string unknown_fields_;\n");
-  p->Outdent();
-  p->Print("};\n\n");
-}
-
-void ProtoToCpp::GenCpp(const Descriptor* msg, Printer* p, std::string prefix) {
-  p->Print("\n");
-  std::string full_name = prefix + msg->name();
-
-  p->Print("$f$::$n$() = default;\n", "f", full_name, "n", msg->name());
-  p->Print("$f$::~$n$() = default;\n", "f", full_name, "n", msg->name());
-  p->Print("$f$::$n$(const $f$&) = default;\n", "f", full_name, "n",
-           msg->name());
-  p->Print("$f$& $f$::operator=(const $f$&) = default;\n", "f", full_name, "n",
-           msg->name());
-  p->Print("$f$::$n$($f$&&) noexcept = default;\n", "f", full_name, "n",
-           msg->name());
-  p->Print("$f$& $f$::operator=($f$&&) = default;\n", "f", full_name, "n",
-           msg->name());
-
-  p->Print("\n");
-
-  // Comparison operator
-  p->Print("#pragma GCC diagnostic push\n");
-  p->Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n");
-  p->Print("bool $f$::operator==(const $f$& other) const {\n", "f", full_name,
-           "n", msg->name());
-  p->Indent();
-
-  p->Print("return ");
-  for (int i = 0; i < msg->field_count(); i++) {
-    if (i > 0)
-      p->Print("\n && ");
-    const FieldDescriptor* field = msg->field(i);
-    p->Print("($n$_ == other.$n$_)", "n", field->name());
-  }
-  p->Print(";");
-  p->Outdent();
-  p->Print("}\n");
-  p->Print("#pragma GCC diagnostic pop\n\n");
-
-  std::string proto_type = GetFwdDeclType(msg, true);
-
-  // Genrate the ParseRawProto() method definition.
-  p->Print("void $f$::ParseRawProto(const std::string& raw) {\n", "f",
-           full_name);
-  p->Indent();
-  p->Print("$p$ proto;\n", "p", proto_type);
-  p->Print("proto.ParseFromString(raw);\n");
-  p->Print("FromProto(proto);\n");
-  p->Outdent();
-  p->Print("}\n\n");
-
-  // Genrate the FromProto() method definition.
-  p->Print("void $f$::FromProto(const $p$& proto) {\n", "f", full_name, "p",
-           proto_type);
-  p->Indent();
-  for (int i = 0; i < msg->field_count(); i++) {
-    p->Print("\n");
-    const FieldDescriptor* field = msg->field(i);
-    if (field->options().lazy()) {
-      p->Print("$n$_ = proto.$n$().SerializeAsString();\n", "n", field->name());
-    } else if (!field->is_repeated()) {
-      if (field->type() == TYPE_MESSAGE) {
-        p->Print("$n$_->FromProto(proto.$n$());\n", "n", field->name());
-      } else {
-        p->Print(
-            "static_assert(sizeof($n$_) == sizeof(proto.$n$()), \"size "
-            "mismatch\");\n",
-            "n", field->name());
-        p->Print("$n$_ = static_cast<decltype($n$_)>(proto.$n$());\n", "n",
-                 field->name());
-      }
-    } else {  // is_repeated()
-      p->Print("$n$_.clear();\n", "n", field->name());
-      p->Print("for (const auto& field : proto.$n$()) {\n", "n", field->name());
-      p->Print("  $n$_.emplace_back();\n", "n", field->name());
-      if (field->type() == TYPE_MESSAGE) {
-        p->Print("  $n$_.back().FromProto(field);\n", "n", field->name());
-      } else {
-        p->Print(
-            "static_assert(sizeof($n$_.back()) == sizeof(proto.$n$(0)), \"size "
-            "mismatch\");\n",
-            "n", field->name());
-        p->Print(
-            "  $n$_.back() = static_cast<decltype($n$_)::value_type>(field);\n",
-            "n", field->name());
-      }
-      p->Print("}\n");
-    }
-  }
-  p->Print("unknown_fields_ = proto.unknown_fields();\n");
-  p->Outdent();
-  p->Print("}\n\n");
-
-  // Genrate the ToProto() method definition.
-  p->Print("void $f$::ToProto($p$* proto) const {\n", "f", full_name, "p",
-           proto_type);
-  p->Indent();
-  p->Print("proto->Clear();\n");
-  for (int i = 0; i < msg->field_count(); i++) {
-    p->Print("\n");
-    const FieldDescriptor* field = msg->field(i);
-    if (field->options().lazy()) {
-      p->Print("proto->mutable_$n$()->ParseFromString($n$_);\n", "n",
-               field->name());
-    } else if (!field->is_repeated()) {
-      if (field->type() == TYPE_MESSAGE) {
-        p->Print("$n$_->ToProto(proto->mutable_$n$());\n", "n", field->name());
-      } else {
-        p->Print(
-            "static_assert(sizeof($n$_) == sizeof(proto->$n$()), \"size "
-            "mismatch\");\n",
-            "n", field->name());
-        p->Print("proto->set_$n$(static_cast<decltype(proto->$n$())>($n$_));\n",
-                 "n", field->name());
-      }
-    } else {  // is_repeated()
-      p->Print("for (const auto& it : $n$_) {\n", "n", field->name());
-      if (field->type() == TYPE_MESSAGE) {
-        p->Print("  auto* entry = proto->add_$n$();\n", "n", field->name());
-        p->Print("  it.ToProto(entry);\n");
-      } else {
-        p->Print(
-            "  proto->add_$n$(static_cast<decltype(proto->$n$(0))>(it));\n",
-            "n", field->name());
-        p->Print(
-            "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
-            "mismatch\");\n",
-            "n", field->name());
-      }
-      p->Print("}\n");
-    }
-  }
-  p->Print("*(proto->mutable_unknown_fields()) = unknown_fields_;\n");
-  p->Outdent();
-  p->Print("}\n\n");
-
-  // Recurse into nested types.
-  for (int i = 0; i < msg->field_count(); i++) {
-    const FieldDescriptor* field = msg->field(i);
-    // [lazy=true] field are not recursesd and exposed as raw-strings
-    // containing proto-encoded bytes.
-    if (field->options().lazy())
-      continue;
-
-    if (field->type() == TYPE_MESSAGE &&
-        field->message_type()->file() == msg->file()) {
-      std::string child_prefix = prefix + msg->name() + "::";
-      GenCpp(field->message_type(), p, child_prefix);
-    }
-  }
-}
-
-int main(int argc, char** argv) {
-  if (argc <= 4) {
-    PERFETTO_ELOG(
-        "Usage: %s source.proto dir/for/header dir/for/cc include/path",
-        argv[0]);
-    return 1;
-  }
-  ProtoToCpp proto_to_cpp(argv[2], argv[3], argv[4]);
-  proto_to_cpp.Convert(argv[1]);
-  return 0;
-}