protozero: rename protoc_plugin -> protozero_plugin (and ipc)

Non functional refactoring. Just changes the name of the
plugin executable and gets rid of the extra .h/.cc in the plugin
code. This keeps it consistent with the ipc plugin and allows to
disambiguate with the upcoming cpp stub generator.

Bug: 132880619
Change-Id: Ib940a8689353677d7c2c5e5f64880ea97c5290e0
diff --git a/Android.bp b/Android.bp
index 5a6eee3..48445ad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1021,9 +1021,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/common/android_log_constants.pbzero.cc",
     "external/perfetto/protos/perfetto/common/commit_data_request.pbzero.cc",
@@ -1051,9 +1051,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/common/android_log_constants.pbzero.h",
     "external/perfetto/protos/perfetto/common/commit_data_request.pbzero.h",
@@ -1170,9 +1170,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/android/android_log_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/android/packages_list_config.pbzero.cc",
@@ -1210,9 +1210,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/android/android_log_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/android/packages_list_config.pbzero.h",
@@ -1290,9 +1290,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/metrics/android/batt_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.cc",
@@ -1318,9 +1318,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/metrics/android/batt_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.h",
@@ -1343,9 +1343,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/metrics/metrics.pbzero.cc",
   ],
@@ -1359,9 +1359,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/metrics/metrics.pbzero.h",
   ],
@@ -1416,9 +1416,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.cc",
@@ -1434,9 +1434,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pbzero.h",
     "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.h",
@@ -1497,9 +1497,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.cc",
@@ -1517,9 +1517,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h",
     "external/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.h",
@@ -1571,9 +1571,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/filesystem/inode_file_map.pbzero.cc",
   ],
@@ -1587,9 +1587,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/filesystem/inode_file_map.pbzero.h",
   ],
@@ -1799,9 +1799,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ftrace/binder.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/block.pbzero.cc",
@@ -1879,9 +1879,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ftrace/binder.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ftrace/block.pbzero.h",
@@ -1968,9 +1968,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/gpu/gpu_counter_event.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.cc",
@@ -1986,9 +1986,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/gpu/gpu_counter_event.pbzero.h",
     "external/perfetto/protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.h",
@@ -2039,9 +2039,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.cc",
   ],
@@ -2055,9 +2055,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h",
   ],
@@ -2189,9 +2189,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.cc",
   ],
@@ -2205,9 +2205,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h",
   ],
@@ -2262,9 +2262,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/power/battery_counters.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/power/power_rails.pbzero.cc",
@@ -2280,9 +2280,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/power/battery_counters.pbzero.h",
     "external/perfetto/protos/perfetto/trace/power/power_rails.pbzero.h",
@@ -2300,9 +2300,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace_processor/metrics_impl.pbzero.cc",
   ],
@@ -2316,9 +2316,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace_processor/metrics_impl.pbzero.h",
   ],
@@ -2373,9 +2373,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.cc",
@@ -2391,9 +2391,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pbzero.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.h",
@@ -2449,9 +2449,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ps/process_stats.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ps/process_tree.pbzero.cc",
@@ -2467,9 +2467,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ps/process_stats.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ps/process_tree.pbzero.h",
@@ -2520,9 +2520,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/sys_stats/sys_stats.pbzero.cc",
   ],
@@ -2536,9 +2536,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/sys_stats/sys_stats.pbzero.h",
   ],
@@ -2608,9 +2608,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.cc",
@@ -2632,9 +2632,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h",
@@ -2693,9 +2693,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/system_info.pbzero.cc",
@@ -2719,9 +2719,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pbzero.h",
     "external/perfetto/protos/perfetto/trace/system_info.pbzero.h",
@@ -2772,7 +2772,6 @@
 cc_binary_host {
   name: "perfetto_src_ipc_protoc_plugin_ipc_plugin___gn_standalone_toolchain_gcc_like_host_",
   srcs: [
-    "src/ipc/protoc_plugin/ipc_generator.cc",
     "src/ipc/protoc_plugin/ipc_plugin.cc",
   ],
   shared_libs: [
@@ -2903,11 +2902,10 @@
   ],
 }
 
-// GN target: //src/protozero/protoc_plugin:protoc_plugin(//gn/standalone/toolchain:gcc_like_host)
+// GN target: //src/protozero/protoc_plugin:protozero_plugin(//gn/standalone/toolchain:gcc_like_host)
 cc_binary_host {
-  name: "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  name: "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   srcs: [
-    "src/protozero/protoc_plugin/protozero_generator.cc",
     "src/protozero/protoc_plugin/protozero_plugin.cc",
   ],
   shared_libs: [
@@ -2979,9 +2977,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
   out: [
     "external/perfetto/src/protozero/test/example_proto/library.pbzero.cc",
     "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pbzero.cc",
@@ -3001,9 +2999,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
   out: [
     "external/perfetto/src/protozero/test/example_proto/library.pbzero.h",
     "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pbzero.h",
@@ -3056,9 +3054,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
   out: [
     "external/perfetto/src/traced/probes/ftrace/test/test_messages.pbzero.cc",
   ],
@@ -3072,9 +3070,9 @@
   ],
   tools: [
     "aprotoc",
-    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+    "perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto --proto_path=external/perfetto/ --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protozero_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto $(in)",
   out: [
     "external/perfetto/src/traced/probes/ftrace/test/test_messages.pbzero.h",
   ],
diff --git a/BUILD b/BUILD
index 97d810d..631e697 100644
--- a/BUILD
+++ b/BUILD
@@ -107,12 +107,10 @@
     ],
 )
 
-# GN target: //src/protozero/protoc_plugin:protoc_plugin
+# GN target: //src/protozero/protoc_plugin:protozero_plugin
 cc_binary(
-    name = "src_protozero_protoc_plugin_protoc_plugin",
+    name = "src_protozero_protoc_plugin_protozero_plugin",
     srcs = [
-        "src/protozero/protoc_plugin/protozero_generator.cc",
-        "src/protozero/protoc_plugin/protozero_generator.h",
         "src/protozero/protoc_plugin/protozero_plugin.cc",
     ],
     deps = [
diff --git a/BUILD.gn b/BUILD.gn
index 74ca6bb..eeb8f7d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -46,7 +46,7 @@
   testonly = true  # allow to build also test targets
   deps = [
     ":perfetto_unittests",
-    "src/protozero/protoc_plugin($host_toolchain)",
+    "src/protozero/protoc_plugin:protozero_plugin($host_toolchain)",
   ]
   if (perfetto_build_standalone || perfetto_build_with_android ||
       build_with_chromium) {
diff --git a/gn/protozero_library.gni b/gn/protozero_library.gni
index d2c01b9..b6ac819 100644
--- a/gn/protozero_library.gni
+++ b/gn/protozero_library.gni
@@ -25,7 +25,8 @@
 
     generate_cc = false
     generate_python = false
-    generator_plugin_label = perfetto_root_path + "src/protozero/protoc_plugin"
+    generator_plugin_label =
+        perfetto_root_path + "src/protozero/protoc_plugin:protozero_plugin"
     generator_plugin_suffix = ".pbzero"
     if (build_with_chromium) {
       component_build_force_source_set = true
diff --git a/src/ipc/protoc_plugin/BUILD.gn b/src/ipc/protoc_plugin/BUILD.gn
index 38424f9..5416566 100644
--- a/src/ipc/protoc_plugin/BUILD.gn
+++ b/src/ipc/protoc_plugin/BUILD.gn
@@ -15,8 +15,6 @@
 if (current_toolchain == host_toolchain) {
   executable("ipc_plugin") {
     sources = [
-      "ipc_generator.cc",
-      "ipc_generator.h",
       "ipc_plugin.cc",
     ]
     deps = [
diff --git a/src/ipc/protoc_plugin/ipc_generator.cc b/src/ipc/protoc_plugin/ipc_generator.cc
deleted file mode 100644
index c955cb1..0000000
--- a/src/ipc/protoc_plugin/ipc_generator.cc
+++ /dev/null
@@ -1,275 +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 "src/ipc/protoc_plugin/ipc_generator.h"
-
-#include <functional>
-#include <memory>
-#include <set>
-#include <string>
-
-#include <google/protobuf/compiler/cpp/cpp_options.h>
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/descriptor.pb.h>
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream.h>
-#include <google/protobuf/stubs/strutil.h>
-
-namespace perfetto {
-namespace ipc {
-
-using google::protobuf::ServiceDescriptor;
-using google::protobuf::FileDescriptor;
-using google::protobuf::MethodDescriptor;
-using google::protobuf::compiler::GeneratorContext;
-using google::protobuf::io::Printer;
-using google::protobuf::io::ZeroCopyOutputStream;
-
-using google::protobuf::Split;
-using google::protobuf::StripString;
-using google::protobuf::StripSuffixString;
-using google::protobuf::UpperString;
-
-namespace {
-
-static const char kBanner[] = "// DO NOT EDIT. Autogenerated by Perfetto IPC\n";
-
-static const char kHeaderSvcClass[] = R"(
-class $c$ : public ::perfetto::ipc::Service {
- private:
-  static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();
-
- public:
-  ~$c$() override;
-
-  static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();
-
-  // Service implementation.
-  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
-
-  // Methods from the .proto file
-)";
-
-static const char kHeaderProxyClass[] = R"(
-class $c$Proxy : public ::perfetto::ipc::ServiceProxy {
- public:
-   explicit $c$Proxy(::perfetto::ipc::ServiceProxy::EventListener*);
-   ~$c$Proxy() override;
-
-  // ServiceProxy implementation.
-  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
-
-  // Methods from the .proto file
-)";
-
-static const char kCppClassDefinitions[] = R"(
-const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptorStatic() {
-  static auto* instance = NewDescriptor();
-  return *instance;
-}
-
-// Host-side definitions.
-$c$::~$c$() = default;
-
-const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptor() {
-  return GetDescriptorStatic();
-}
-
-// Client-side definitions.
-$c$Proxy::$c$Proxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
-    : ::perfetto::ipc::ServiceProxy(event_listener) {}
-
-$c$Proxy::~$c$Proxy() = default;
-
-const ::perfetto::ipc::ServiceDescriptor& $c$Proxy::GetDescriptor() {
-  return $c$::GetDescriptorStatic();
-}
-)";
-
-static const char kCppMethodDescriptor[] = R"(
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "$m$",
-     &_IPC_Decoder<$i$>,
-     &_IPC_Decoder<$o$>,
-     &_IPC_Invoker<$c$, $i$, $o$, &$c$::$m$>});
-)";
-
-static const char kCppMethod[] = R"(
-void $c$Proxy::$m$(const $i$& request, Deferred$o$ reply, int fd) {
-  BeginInvoke("$m$", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-)";
-
-std::string StripName(const FileDescriptor& file) {
-  return StripSuffixString(file.name(), ".proto");
-}
-
-std::string GetStubName(const FileDescriptor& file) {
-  return StripName(file) + ".ipc";
-}
-
-void ForEachMethod(const ServiceDescriptor& svc,
-                   std::function<void(const MethodDescriptor&,
-                                      const std::string&,
-                                      const std::string&)> function) {
-  for (int i = 0; i < svc.method_count(); i++) {
-    const MethodDescriptor& method = *svc.method(i);
-    // TODO if the input or output type are in a different namespace we need to
-    // emit the ::fully::qualified::name.
-    std::string input_type = method.input_type()->name();
-    std::string output_type = method.output_type()->name();
-    function(method, input_type, output_type);
-  }
-}
-
-void GenerateServiceHeader(const FileDescriptor& file,
-                           const ServiceDescriptor& svc,
-                           Printer* printer) {
-  printer->Print("\n");
-  std::vector<std::string> namespaces = Split(file.package(), ".");
-  for (const std::string& ns : namespaces)
-    printer->Print("namespace $ns$ {\n", "ns", ns);
-
-  // Generate the host-side declarations.
-  printer->Print(kHeaderSvcClass, "c", svc.name());
-  std::set<std::string> types_seen;
-  ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method,
-                                            const std::string& input_type,
-                                            const std::string& output_type) {
-    if (types_seen.count(output_type) == 0) {
-      printer->Print("  using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n",
-                     "o", output_type);
-      types_seen.insert(output_type);
-    }
-    printer->Print("  virtual void $m$(const $i$&, Deferred$o$) = 0;\n\n", "m",
-                   method.name(), "i", input_type, "o", output_type);
-  });
-  printer->Print("};\n\n");
-
-  // Generate the client-side declarations.
-  printer->Print(kHeaderProxyClass, "c", svc.name());
-  types_seen.clear();
-  ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method,
-                                            const std::string& input_type,
-                                            const std::string& output_type) {
-    if (types_seen.count(output_type) == 0) {
-      printer->Print("  using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n",
-                     "o", output_type);
-      types_seen.insert(output_type);
-    }
-    printer->Print("  void $m$(const $i$&, Deferred$o$, int fd = -1);\n\n", "m",
-                   method.name(), "i", input_type, "o", output_type);
-  });
-  printer->Print("};\n\n");
-
-  for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++)
-    printer->Print("}  // namespace $ns$\n", "ns", *it);
-
-  printer->Print("\n");
-}
-
-void GenerateServiceCpp(const FileDescriptor& file,
-                        const ServiceDescriptor& svc,
-                        Printer* printer) {
-  printer->Print("\n");
-
-  std::vector<std::string> namespaces = Split(file.package(), ".");
-  for (const std::string& ns : namespaces)
-    printer->Print("namespace $ns$ {\n", "ns", ns);
-
-  printer->Print("::perfetto::ipc::ServiceDescriptor* $c$::NewDescriptor() {\n",
-                 "c", svc.name());
-  printer->Print("  auto* desc = new ::perfetto::ipc::ServiceDescriptor();\n");
-  printer->Print("  desc->service_name = \"$c$\";\n", "c", svc.name());
-
-  ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method,
-                                     const std::string& input_type,
-                                     const std::string& output_type) {
-    printer->Print(kCppMethodDescriptor, "c", svc.name(), "i", input_type, "o",
-                   output_type, "m", method.name());
-  });
-
-  printer->Print("  desc->methods.shrink_to_fit();\n");
-  printer->Print("  return desc;\n");
-  printer->Print("}\n\n");
-
-  printer->Print(kCppClassDefinitions, "c", svc.name());
-
-  ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method,
-                                     const std::string& input_type,
-                                     const std::string& output_type) {
-    printer->Print(kCppMethod, "c", svc.name(), "m", method.name(), "i",
-                   input_type, "o", output_type);
-  });
-
-  for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++)
-    printer->Print("}  // namespace $ns$\n", "ns", *it);
-}
-
-}  // namespace
-
-IPCGenerator::IPCGenerator() = default;
-IPCGenerator::~IPCGenerator() = default;
-
-bool IPCGenerator::Generate(const FileDescriptor* file,
-                            const std::string& /*options*/,
-                            GeneratorContext* context,
-                            std::string* error) const {
-  if (file->options().cc_generic_services()) {
-    *error = "Please set \"cc_generic_service = false\".";
-    return false;
-  }
-
-  const std::unique_ptr<ZeroCopyOutputStream> h_fstream(
-      context->Open(GetStubName(*file) + ".h"));
-  const std::unique_ptr<ZeroCopyOutputStream> cc_fstream(
-      context->Open(GetStubName(*file) + ".cc"));
-
-  // Variables are delimited by $.
-  Printer h_printer(h_fstream.get(), '$');
-  Printer cc_printer(cc_fstream.get(), '$');
-
-  std::string guard = file->package() + "_" + file->name() + "_H_";
-  UpperString(&guard);
-  StripString(&guard, ".-/\\", '_');
-
-  h_printer.Print(kBanner);
-  h_printer.Print("#ifndef $guard$\n#define $guard$\n\n", "guard", guard);
-  h_printer.Print("#include \"$h$\"\n", "h", StripName(*file) + ".pb.h");
-  h_printer.Print("#include \"perfetto/ext/ipc/deferred.h\"\n");
-  h_printer.Print("#include \"perfetto/ext/ipc/service.h\"\n");
-  h_printer.Print("#include \"perfetto/ext/ipc/service_descriptor.h\"\n");
-  h_printer.Print("#include \"perfetto/ext/ipc/service_proxy.h\"\n\n");
-
-  cc_printer.Print(kBanner);
-  cc_printer.Print("#include \"$h$\"\n", "h", GetStubName(*file) + ".h");
-  cc_printer.Print("#include \"perfetto/ext/ipc/codegen_helpers.h\"\n\n");
-  cc_printer.Print("#include <memory>\n");
-
-  for (int i = 0; i < file->service_count(); i++) {
-    const ServiceDescriptor* svc = file->service(i);
-    GenerateServiceHeader(*file, *svc, &h_printer);
-    GenerateServiceCpp(*file, *svc, &cc_printer);
-  }
-
-  h_printer.Print("#endif  // $guard$\n", "guard", guard);
-
-  return true;
-}
-
-}  // namespace ipc
-}  // namespace perfetto
diff --git a/src/ipc/protoc_plugin/ipc_generator.h b/src/ipc/protoc_plugin/ipc_generator.h
deleted file mode 100644
index fa8f76a..0000000
--- a/src/ipc/protoc_plugin/ipc_generator.h
+++ /dev/null
@@ -1,42 +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.
- */
-
-#ifndef SRC_IPC_PROTOC_PLUGIN_IPC_GENERATOR_H_
-#define SRC_IPC_PROTOC_PLUGIN_IPC_GENERATOR_H_
-
-#include <string>
-
-#include <google/protobuf/compiler/code_generator.h>
-
-namespace perfetto {
-namespace ipc {
-
-class IPCGenerator : public ::google::protobuf::compiler::CodeGenerator {
- public:
-  explicit IPCGenerator();
-  ~IPCGenerator() override;
-
-  // CodeGenerator implementation
-  bool Generate(const google::protobuf::FileDescriptor* file,
-                const std::string& options,
-                google::protobuf::compiler::GeneratorContext* context,
-                std::string* error) const override;
-};
-
-}  // namespace ipc
-}  // namespace perfetto
-
-#endif  // SRC_IPC_PROTOC_PLUGIN_IPC_GENERATOR_H_
diff --git a/src/ipc/protoc_plugin/ipc_plugin.cc b/src/ipc/protoc_plugin/ipc_plugin.cc
index 98280ce..064e4cd 100644
--- a/src/ipc/protoc_plugin/ipc_plugin.cc
+++ b/src/ipc/protoc_plugin/ipc_plugin.cc
@@ -14,8 +14,274 @@
  * limitations under the License.
  */
 
+#include <functional>
+#include <memory>
+#include <set>
+#include <string>
+
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/compiler/cpp/cpp_options.h>
 #include <google/protobuf/compiler/plugin.h>
-#include "src/ipc/protoc_plugin/ipc_generator.h"
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace perfetto {
+namespace ipc {
+namespace {
+
+using google::protobuf::FileDescriptor;
+using google::protobuf::MethodDescriptor;
+using google::protobuf::ServiceDescriptor;
+using google::protobuf::Split;
+using google::protobuf::StripString;
+using google::protobuf::StripSuffixString;
+using google::protobuf::UpperString;
+using google::protobuf::compiler::GeneratorContext;
+using google::protobuf::io::Printer;
+using google::protobuf::io::ZeroCopyOutputStream;
+
+static const char kBanner[] = "// DO NOT EDIT. Autogenerated by Perfetto IPC\n";
+
+static const char kHeaderSvcClass[] = R"(
+class $c$ : public ::perfetto::ipc::Service {
+ private:
+  static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();
+
+ public:
+  ~$c$() override;
+
+  static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();
+
+  // Service implementation.
+  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
+
+  // Methods from the .proto file
+)";
+
+static const char kHeaderProxyClass[] = R"(
+class $c$Proxy : public ::perfetto::ipc::ServiceProxy {
+ public:
+   explicit $c$Proxy(::perfetto::ipc::ServiceProxy::EventListener*);
+   ~$c$Proxy() override;
+
+  // ServiceProxy implementation.
+  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
+
+  // Methods from the .proto file
+)";
+
+static const char kCppClassDefinitions[] = R"(
+const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptorStatic() {
+  static auto* instance = NewDescriptor();
+  return *instance;
+}
+
+// Host-side definitions.
+$c$::~$c$() = default;
+
+const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptor() {
+  return GetDescriptorStatic();
+}
+
+// Client-side definitions.
+$c$Proxy::$c$Proxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
+    : ::perfetto::ipc::ServiceProxy(event_listener) {}
+
+$c$Proxy::~$c$Proxy() = default;
+
+const ::perfetto::ipc::ServiceDescriptor& $c$Proxy::GetDescriptor() {
+  return $c$::GetDescriptorStatic();
+}
+)";
+
+static const char kCppMethodDescriptor[] = R"(
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "$m$",
+     &_IPC_Decoder<$i$>,
+     &_IPC_Decoder<$o$>,
+     &_IPC_Invoker<$c$, $i$, $o$, &$c$::$m$>});
+)";
+
+static const char kCppMethod[] = R"(
+void $c$Proxy::$m$(const $i$& request, Deferred$o$ reply, int fd) {
+  BeginInvoke("$m$", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}
+)";
+
+std::string StripName(const FileDescriptor& file) {
+  return StripSuffixString(file.name(), ".proto");
+}
+
+std::string GetStubName(const FileDescriptor& file) {
+  return StripName(file) + ".ipc";
+}
+
+void ForEachMethod(const ServiceDescriptor& svc,
+                   std::function<void(const MethodDescriptor&,
+                                      const std::string&,
+                                      const std::string&)> function) {
+  for (int i = 0; i < svc.method_count(); i++) {
+    const MethodDescriptor& method = *svc.method(i);
+    // TODO if the input or output type are in a different namespace we need to
+    // emit the ::fully::qualified::name.
+    std::string input_type = method.input_type()->name();
+    std::string output_type = method.output_type()->name();
+    function(method, input_type, output_type);
+  }
+}
+
+void GenerateServiceHeader(const FileDescriptor& file,
+                           const ServiceDescriptor& svc,
+                           Printer* printer) {
+  printer->Print("\n");
+  std::vector<std::string> namespaces = Split(file.package(), ".");
+  for (const std::string& ns : namespaces)
+    printer->Print("namespace $ns$ {\n", "ns", ns);
+
+  // Generate the host-side declarations.
+  printer->Print(kHeaderSvcClass, "c", svc.name());
+  std::set<std::string> types_seen;
+  ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method,
+                                            const std::string& input_type,
+                                            const std::string& output_type) {
+    if (types_seen.count(output_type) == 0) {
+      printer->Print("  using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n",
+                     "o", output_type);
+      types_seen.insert(output_type);
+    }
+    printer->Print("  virtual void $m$(const $i$&, Deferred$o$) = 0;\n\n", "m",
+                   method.name(), "i", input_type, "o", output_type);
+  });
+  printer->Print("};\n\n");
+
+  // Generate the client-side declarations.
+  printer->Print(kHeaderProxyClass, "c", svc.name());
+  types_seen.clear();
+  ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method,
+                                            const std::string& input_type,
+                                            const std::string& output_type) {
+    if (types_seen.count(output_type) == 0) {
+      printer->Print("  using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n",
+                     "o", output_type);
+      types_seen.insert(output_type);
+    }
+    printer->Print("  void $m$(const $i$&, Deferred$o$, int fd = -1);\n\n", "m",
+                   method.name(), "i", input_type, "o", output_type);
+  });
+  printer->Print("};\n\n");
+
+  for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++)
+    printer->Print("}  // namespace $ns$\n", "ns", *it);
+
+  printer->Print("\n");
+}
+
+void GenerateServiceCpp(const FileDescriptor& file,
+                        const ServiceDescriptor& svc,
+                        Printer* printer) {
+  printer->Print("\n");
+
+  std::vector<std::string> namespaces = Split(file.package(), ".");
+  for (const std::string& ns : namespaces)
+    printer->Print("namespace $ns$ {\n", "ns", ns);
+
+  printer->Print("::perfetto::ipc::ServiceDescriptor* $c$::NewDescriptor() {\n",
+                 "c", svc.name());
+  printer->Print("  auto* desc = new ::perfetto::ipc::ServiceDescriptor();\n");
+  printer->Print("  desc->service_name = \"$c$\";\n", "c", svc.name());
+
+  ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method,
+                                     const std::string& input_type,
+                                     const std::string& output_type) {
+    printer->Print(kCppMethodDescriptor, "c", svc.name(), "i", input_type, "o",
+                   output_type, "m", method.name());
+  });
+
+  printer->Print("  desc->methods.shrink_to_fit();\n");
+  printer->Print("  return desc;\n");
+  printer->Print("}\n\n");
+
+  printer->Print(kCppClassDefinitions, "c", svc.name());
+
+  ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method,
+                                     const std::string& input_type,
+                                     const std::string& output_type) {
+    printer->Print(kCppMethod, "c", svc.name(), "m", method.name(), "i",
+                   input_type, "o", output_type);
+  });
+
+  for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++)
+    printer->Print("}  // namespace $ns$\n", "ns", *it);
+}
+
+class IPCGenerator : public ::google::protobuf::compiler::CodeGenerator {
+ public:
+  explicit IPCGenerator();
+  ~IPCGenerator() override;
+
+  // CodeGenerator implementation
+  bool Generate(const google::protobuf::FileDescriptor* file,
+                const std::string& options,
+                google::protobuf::compiler::GeneratorContext* context,
+                std::string* error) const override;
+};
+
+IPCGenerator::IPCGenerator() = default;
+IPCGenerator::~IPCGenerator() = default;
+
+bool IPCGenerator::Generate(const FileDescriptor* file,
+                            const std::string& /*options*/,
+                            GeneratorContext* context,
+                            std::string* error) const {
+  if (file->options().cc_generic_services()) {
+    *error = "Please set \"cc_generic_service = false\".";
+    return false;
+  }
+
+  const std::unique_ptr<ZeroCopyOutputStream> h_fstream(
+      context->Open(GetStubName(*file) + ".h"));
+  const std::unique_ptr<ZeroCopyOutputStream> cc_fstream(
+      context->Open(GetStubName(*file) + ".cc"));
+
+  // Variables are delimited by $.
+  Printer h_printer(h_fstream.get(), '$');
+  Printer cc_printer(cc_fstream.get(), '$');
+
+  std::string guard = file->package() + "_" + file->name() + "_H_";
+  UpperString(&guard);
+  StripString(&guard, ".-/\\", '_');
+
+  h_printer.Print(kBanner);
+  h_printer.Print("#ifndef $guard$\n#define $guard$\n\n", "guard", guard);
+  h_printer.Print("#include \"$h$\"\n", "h", StripName(*file) + ".pb.h");
+  h_printer.Print("#include \"perfetto/ext/ipc/deferred.h\"\n");
+  h_printer.Print("#include \"perfetto/ext/ipc/service.h\"\n");
+  h_printer.Print("#include \"perfetto/ext/ipc/service_descriptor.h\"\n");
+  h_printer.Print("#include \"perfetto/ext/ipc/service_proxy.h\"\n\n");
+
+  cc_printer.Print(kBanner);
+  cc_printer.Print("#include \"$h$\"\n", "h", GetStubName(*file) + ".h");
+  cc_printer.Print("#include \"perfetto/ext/ipc/codegen_helpers.h\"\n\n");
+  cc_printer.Print("#include <memory>\n");
+
+  for (int i = 0; i < file->service_count(); i++) {
+    const ServiceDescriptor* svc = file->service(i);
+    GenerateServiceHeader(*file, *svc, &h_printer);
+    GenerateServiceCpp(*file, *svc, &cc_printer);
+  }
+
+  h_printer.Print("#endif  // $guard$\n", "guard", guard);
+
+  return true;
+}
+
+}  // namespace
+}  // namespace ipc
+}  // namespace perfetto
 
 int main(int argc, char* argv[]) {
   ::perfetto::ipc::IPCGenerator generator;
diff --git a/src/protozero/protoc_plugin/BUILD.gn b/src/protozero/protoc_plugin/BUILD.gn
index 2466ea6..88139ed 100644
--- a/src/protozero/protoc_plugin/BUILD.gn
+++ b/src/protozero/protoc_plugin/BUILD.gn
@@ -13,10 +13,8 @@
 # limitations under the License.
 
 if (current_toolchain == host_toolchain) {
-  executable("protoc_plugin") {
+  executable("protozero_plugin") {
     sources = [
-      "protozero_generator.cc",
-      "protozero_generator.h",
       "protozero_plugin.cc",
     ]
     deps = [
diff --git a/src/protozero/protoc_plugin/protozero_generator.cc b/src/protozero/protoc_plugin/protozero_generator.cc
deleted file mode 100644
index 045203f..0000000
--- a/src/protozero/protoc_plugin/protozero_generator.cc
+++ /dev/null
@@ -1,742 +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 "src/protozero/protoc_plugin/protozero_generator.h"
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream.h>
-#include <google/protobuf/stubs/strutil.h>
-
-namespace protozero {
-
-using google::protobuf::Descriptor;  // Message descriptor.
-using google::protobuf::EnumDescriptor;
-using google::protobuf::EnumValueDescriptor;
-using google::protobuf::FieldDescriptor;
-using google::protobuf::FileDescriptor;
-using google::protobuf::compiler::GeneratorContext;
-using google::protobuf::io::Printer;
-using google::protobuf::io::ZeroCopyOutputStream;
-
-using google::protobuf::Split;
-using google::protobuf::StripPrefixString;
-using google::protobuf::StripString;
-using google::protobuf::StripSuffixString;
-using google::protobuf::UpperString;
-
-namespace {
-
-// Keep this value in sync with ProtoDecoder::kMaxDecoderFieldId. If they go out
-// of sync pbzero.h files will stop compiling, hitting the at() static_assert.
-// Not worth an extra dependency.
-constexpr int kMaxDecoderFieldId = 999;
-
-void Assert(bool condition) {
-  if (!condition)
-    __builtin_trap();
-}
-
-struct FileDescriptorComp {
-  bool operator()(const FileDescriptor* lhs, const FileDescriptor* rhs) const {
-    int comp = lhs->name().compare(rhs->name());
-    Assert(comp != 0 || lhs == rhs);
-    return comp < 0;
-  }
-};
-
-struct DescriptorComp {
-  bool operator()(const Descriptor* lhs, const Descriptor* rhs) const {
-    int comp = lhs->full_name().compare(rhs->full_name());
-    Assert(comp != 0 || lhs == rhs);
-    return comp < 0;
-  }
-};
-
-struct EnumDescriptorComp {
-  bool operator()(const EnumDescriptor* lhs, const EnumDescriptor* rhs) const {
-    int comp = lhs->full_name().compare(rhs->full_name());
-    Assert(comp != 0 || lhs == rhs);
-    return comp < 0;
-  }
-};
-
-inline std::string ProtoStubName(const FileDescriptor* proto) {
-  return StripSuffixString(proto->name(), ".proto") + ".pbzero";
-}
-
-class GeneratorJob {
- public:
-  GeneratorJob(const FileDescriptor* file, Printer* stub_h_printer)
-      : source_(file), stub_h_(stub_h_printer) {}
-
-  bool GenerateStubs() {
-    Preprocess();
-    GeneratePrologue();
-    for (const EnumDescriptor* enumeration : enums_)
-      GenerateEnumDescriptor(enumeration);
-    for (const Descriptor* message : messages_)
-      GenerateMessageDescriptor(message);
-    GenerateEpilogue();
-    return error_.empty();
-  }
-
-  void SetOption(const std::string& name, const std::string& value) {
-    if (name == "wrapper_namespace") {
-      wrapper_namespace_ = value;
-    } else {
-      Abort(std::string() + "Unknown plugin option '" + name + "'.");
-    }
-  }
-
-  // If generator fails to produce stubs for a particular proto definitions
-  // it finishes with undefined output and writes the first error occured.
-  const std::string& GetFirstError() const { return error_; }
-
- private:
-  // Only the first error will be recorded.
-  void Abort(const std::string& reason) {
-    if (error_.empty())
-      error_ = reason;
-  }
-
-  // Get full name (including outer descriptors) of proto descriptor.
-  template <class T>
-  inline std::string GetDescriptorName(const T* descriptor) {
-    if (!package_.empty()) {
-      return StripPrefixString(descriptor->full_name(), package_ + ".");
-    } else {
-      return descriptor->full_name();
-    }
-  }
-
-  // Get C++ class name corresponding to proto descriptor.
-  // Nested names are splitted by underscores. Underscores in type names aren't
-  // prohibited but not recommended in order to avoid name collisions.
-  template <class T>
-  inline std::string GetCppClassName(const T* descriptor, bool full = false) {
-    std::string name = GetDescriptorName(descriptor);
-    StripString(&name, ".", '_');
-    if (full)
-      name = full_namespace_prefix_ + name;
-    return name;
-  }
-
-  inline std::string GetFieldNumberConstant(const FieldDescriptor* field) {
-    std::string name = field->camelcase_name();
-    if (!name.empty()) {
-      name.at(0) = static_cast<char>(toupper(name.at(0)));
-      name = "k" + name + "FieldNumber";
-    } else {
-      // Protoc allows fields like 'bool _ = 1'.
-      Abort("Empty field name in camel case notation.");
-    }
-    return name;
-  }
-
-  // Small enums can be written faster without involving VarInt encoder.
-  inline bool IsTinyEnumField(const FieldDescriptor* field) {
-    if (field->type() != FieldDescriptor::TYPE_ENUM)
-      return false;
-    const EnumDescriptor* enumeration = field->enum_type();
-
-    for (int i = 0; i < enumeration->value_count(); ++i) {
-      int32_t value = enumeration->value(i)->number();
-      if (value < 0 || value > 0x7F)
-        return false;
-    }
-    return true;
-  }
-
-  void CollectDescriptors() {
-    // Collect message descriptors in DFS order.
-    std::vector<const Descriptor*> stack;
-    for (int i = 0; i < source_->message_type_count(); ++i)
-      stack.push_back(source_->message_type(i));
-
-    while (!stack.empty()) {
-      const Descriptor* message = stack.back();
-      stack.pop_back();
-      messages_.push_back(message);
-      for (int i = 0; i < message->nested_type_count(); ++i) {
-        stack.push_back(message->nested_type(i));
-      }
-    }
-
-    // Collect enums.
-    for (int i = 0; i < source_->enum_type_count(); ++i)
-      enums_.push_back(source_->enum_type(i));
-
-    for (const Descriptor* message : messages_) {
-      for (int i = 0; i < message->enum_type_count(); ++i) {
-        enums_.push_back(message->enum_type(i));
-      }
-    }
-  }
-
-  void CollectDependencies() {
-    // Public import basically means that callers only need to import this
-    // proto in order to use the stuff publicly imported by this proto.
-    for (int i = 0; i < source_->public_dependency_count(); ++i)
-      public_imports_.insert(source_->public_dependency(i));
-
-    if (source_->weak_dependency_count() > 0)
-      Abort("Weak imports are not supported.");
-
-    // Sanity check. Collect public imports (of collected imports) in DFS order.
-    // Visibilty for current proto:
-    // - all imports listed in current proto,
-    // - public imports of everything imported (recursive).
-    std::vector<const FileDescriptor*> stack;
-    for (int i = 0; i < source_->dependency_count(); ++i) {
-      const FileDescriptor* import = source_->dependency(i);
-      stack.push_back(import);
-      if (public_imports_.count(import) == 0) {
-        private_imports_.insert(import);
-      }
-    }
-
-    while (!stack.empty()) {
-      const FileDescriptor* import = stack.back();
-      stack.pop_back();
-      // Having imports under different packages leads to unnecessary
-      // complexity with namespaces.
-      if (import->package() != package_)
-        Abort("Imported proto must be in the same package.");
-
-      for (int i = 0; i < import->public_dependency_count(); ++i) {
-        stack.push_back(import->public_dependency(i));
-      }
-    }
-
-    // Collect descriptors of messages and enums used in current proto.
-    // It will be used to generate necessary forward declarations and performed
-    // sanity check guarantees that everything lays in the same namespace.
-    for (const Descriptor* message : messages_) {
-      for (int i = 0; i < message->field_count(); ++i) {
-        const FieldDescriptor* field = message->field(i);
-
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
-          if (public_imports_.count(field->message_type()->file()) == 0) {
-            // Avoid multiple forward declarations since
-            // public imports have been already included.
-            referenced_messages_.insert(field->message_type());
-          }
-        } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
-          if (public_imports_.count(field->enum_type()->file()) == 0) {
-            referenced_enums_.insert(field->enum_type());
-          }
-        }
-      }
-    }
-  }
-
-  void Preprocess() {
-    // Package name maps to a series of namespaces.
-    package_ = source_->package();
-    namespaces_ = Split(package_, ".");
-    if (!wrapper_namespace_.empty())
-      namespaces_.push_back(wrapper_namespace_);
-
-    full_namespace_prefix_ = "::";
-    for (const std::string& ns : namespaces_)
-      full_namespace_prefix_ += ns + "::";
-
-    CollectDescriptors();
-    CollectDependencies();
-  }
-
-  // Print top header, namespaces and forward declarations.
-  void GeneratePrologue() {
-    std::string greeting =
-        "// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.\n";
-    std::string guard = package_ + "_" + source_->name() + "_H_";
-    UpperString(&guard);
-    StripString(&guard, ".-/\\", '_');
-
-    stub_h_->Print(
-        "$greeting$\n"
-        "#ifndef $guard$\n"
-        "#define $guard$\n\n"
-        "#include <stddef.h>\n"
-        "#include <stdint.h>\n\n"
-        "#include \"perfetto/protozero/proto_decoder.h\"\n"
-        "#include \"perfetto/protozero/message.h\"\n",
-        "greeting", greeting, "guard", guard);
-
-    // Print includes for public imports.
-    for (const FileDescriptor* dependency : public_imports_) {
-      // Dependency name could contain slashes but importing from upper-level
-      // directories is not possible anyway since build system processes each
-      // proto file individually. Hence proto lookup path is always equal to the
-      // directory where particular proto file is located and protoc does not
-      // allow reference to upper directory (aka ..) in import path.
-      //
-      // Laconically said:
-      // - source_->name() may never have slashes,
-      // - dependency->name() may have slashes but always refers to inner path.
-      stub_h_->Print("#include \"$name$.h\"\n", "name",
-                     ProtoStubName(dependency));
-    }
-    stub_h_->Print("\n");
-
-    // Print namespaces.
-    for (const std::string& ns : namespaces_) {
-      stub_h_->Print("namespace $ns$ {\n", "ns", ns);
-    }
-    stub_h_->Print("\n");
-
-    // Print forward declarations.
-    for (const Descriptor* message : referenced_messages_) {
-      stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
-    }
-    for (const EnumDescriptor* enumeration : referenced_enums_) {
-      stub_h_->Print("enum $class$ : int32_t;\n", "class",
-                     GetCppClassName(enumeration));
-    }
-    stub_h_->Print("\n");
-  }
-
-  void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
-    stub_h_->Print("enum $class$ : int32_t {\n", "class",
-                   GetCppClassName(enumeration));
-    stub_h_->Indent();
-
-    std::string value_name_prefix;
-    if (enumeration->containing_type() != nullptr)
-      value_name_prefix = GetCppClassName(enumeration) + "_";
-
-    std::string min_name, max_name;
-    int min_val = std::numeric_limits<int>::max();
-    int max_val = -1;
-    for (int i = 0; i < enumeration->value_count(); ++i) {
-      const EnumValueDescriptor* value = enumeration->value(i);
-      stub_h_->Print("$name$ = $number$,\n", "name",
-                     value_name_prefix + value->name(), "number",
-                     std::to_string(value->number()));
-      if (value->number() < min_val) {
-        min_val = value->number();
-        min_name = value_name_prefix + value->name();
-      }
-      if (value->number() > max_val) {
-        max_val = value->number();
-        max_name = value_name_prefix + value->name();
-      }
-    }
-    stub_h_->Outdent();
-    stub_h_->Print("};\n\n");
-    stub_h_->Print("const $class$ $class$_MIN = $min$;\n", "class",
-                   GetCppClassName(enumeration), "min", min_name);
-    stub_h_->Print("const $class$ $class$_MAX = $max$;\n", "class",
-                   GetCppClassName(enumeration), "max", max_name);
-    stub_h_->Print("\n");
-  }
-
-  void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
-    std::map<std::string, std::string> setter;
-    setter["id"] = std::to_string(field->number());
-    setter["name"] = field->name();
-    setter["action"] = field->is_repeated() ? "add" : "set";
-
-    std::string appender;
-    std::string cpp_type;
-
-    switch (field->type()) {
-      case FieldDescriptor::TYPE_BOOL: {
-        appender = "AppendTinyVarInt";
-        cpp_type = "bool";
-        break;
-      }
-      case FieldDescriptor::TYPE_INT32: {
-        appender = "AppendVarInt";
-        cpp_type = "int32_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_INT64: {
-        appender = "AppendVarInt";
-        cpp_type = "int64_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_UINT32: {
-        appender = "AppendVarInt";
-        cpp_type = "uint32_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_UINT64: {
-        appender = "AppendVarInt";
-        cpp_type = "uint64_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_SINT32: {
-        appender = "AppendSignedVarInt";
-        cpp_type = "int32_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_SINT64: {
-        appender = "AppendSignedVarInt";
-        cpp_type = "int64_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_FIXED32: {
-        appender = "AppendFixed";
-        cpp_type = "uint32_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_FIXED64: {
-        appender = "AppendFixed";
-        cpp_type = "uint64_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_SFIXED32: {
-        appender = "AppendFixed";
-        cpp_type = "int32_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_SFIXED64: {
-        appender = "AppendFixed";
-        cpp_type = "int64_t";
-        break;
-      }
-      case FieldDescriptor::TYPE_FLOAT: {
-        appender = "AppendFixed";
-        cpp_type = "float";
-        break;
-      }
-      case FieldDescriptor::TYPE_DOUBLE: {
-        appender = "AppendFixed";
-        cpp_type = "double";
-        break;
-      }
-      case FieldDescriptor::TYPE_ENUM: {
-        appender = IsTinyEnumField(field) ? "AppendTinyVarInt" : "AppendVarInt";
-        cpp_type = GetCppClassName(field->enum_type(), true);
-        break;
-      }
-      case FieldDescriptor::TYPE_STRING: {
-        appender = "AppendString";
-        cpp_type = "const char*";
-        break;
-      }
-      case FieldDescriptor::TYPE_BYTES: {
-        stub_h_->Print(
-            setter,
-            "void $action$_$name$(const uint8_t* data, size_t size) {\n"
-            "  AppendBytes($id$, data, size);\n"
-            "}\n");
-        return;
-      }
-      case FieldDescriptor::TYPE_GROUP:
-      case FieldDescriptor::TYPE_MESSAGE: {
-        Abort("Unsupported field type.");
-        return;
-      }
-    }
-    setter["appender"] = appender;
-    setter["cpp_type"] = cpp_type;
-    stub_h_->Print(setter,
-                   "void $action$_$name$($cpp_type$ value) {\n"
-                   "  $appender$($id$, value);\n"
-                   "}\n");
-
-    // For strings also generate a variant for non-null terminated strings.
-    if (field->type() == FieldDescriptor::TYPE_STRING) {
-      stub_h_->Print(setter,
-                     "// Doesn't check for null terminator.\n"
-                     "// Expects |value| to be at least |size| long.\n"
-                     "void $action$_$name$($cpp_type$ value, size_t size) {\n"
-                     "  AppendBytes($id$, value, size);\n"
-                     "}\n");
-    }
-  }
-
-  void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
-    std::string action = field->is_repeated() ? "add" : "set";
-    std::string inner_class = GetCppClassName(field->message_type());
-    stub_h_->Print(
-        "template <typename T = $inner_class$> T* $action$_$name$() {\n"
-        "  return BeginNestedMessage<T>($id$);\n"
-        "}\n\n",
-        "id", std::to_string(field->number()), "name", field->name(), "action",
-        action, "inner_class", inner_class);
-  }
-
-  void GenerateDecoder(const Descriptor* message) {
-    int max_field_id = 0;
-    bool has_repeated_fields = false;
-    for (int i = 0; i < message->field_count(); ++i) {
-      const FieldDescriptor* field = message->field(i);
-      if (field->number() > kMaxDecoderFieldId)
-        continue;
-      max_field_id = std::max(max_field_id, field->number());
-      if (field->is_repeated())
-        has_repeated_fields = true;
-    }
-
-    std::string class_name = GetCppClassName(message) + "_Decoder";
-    stub_h_->Print(
-        "class $name$ : public "
-        "::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/$max$, "
-        "/*HAS_REPEATED_FIELDS=*/$rep$> {\n",
-        "name", class_name, "max", std::to_string(max_field_id), "rep",
-        has_repeated_fields ? "true" : "false");
-    stub_h_->Print(" public:\n");
-    stub_h_->Indent();
-    stub_h_->Print(
-        "$name$(const uint8_t* data, size_t len) "
-        ": TypedProtoDecoder(data, len) {}\n",
-        "name", class_name);
-    stub_h_->Print(
-        "explicit $name$(const std::string& raw) : "
-        "TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), "
-        "raw.size()) {}\n",
-        "name", class_name);
-    stub_h_->Print(
-        "explicit $name$(const ::protozero::ConstBytes& raw) : "
-        "TypedProtoDecoder(raw.data, raw.size) {}\n",
-        "name", class_name);
-
-    for (int i = 0; i < message->field_count(); ++i) {
-      const FieldDescriptor* field = message->field(i);
-      if (field->is_packed()) {
-        Abort("Packed repeated fields are not supported.");
-        return;
-      }
-
-      if (field->number() > max_field_id) {
-        stub_h_->Print("// field $name$ omitted because its id is too high\n",
-                       "name", field->name());
-        continue;
-      }
-      std::string getter;
-      std::string cpp_type;
-      switch (field->type()) {
-        case FieldDescriptor::TYPE_BOOL:
-          getter = "as_bool";
-          cpp_type = "bool";
-          break;
-        case FieldDescriptor::TYPE_SFIXED32:
-        case FieldDescriptor::TYPE_SINT32:
-        case FieldDescriptor::TYPE_INT32:
-          getter = "as_int32";
-          cpp_type = "int32_t";
-          break;
-        case FieldDescriptor::TYPE_SFIXED64:
-        case FieldDescriptor::TYPE_SINT64:
-        case FieldDescriptor::TYPE_INT64:
-          getter = "as_int64";
-          cpp_type = "int64_t";
-          break;
-        case FieldDescriptor::TYPE_FIXED32:
-        case FieldDescriptor::TYPE_UINT32:
-          getter = "as_uint32";
-          cpp_type = "uint32_t";
-          break;
-        case FieldDescriptor::TYPE_FIXED64:
-        case FieldDescriptor::TYPE_UINT64:
-          getter = "as_uint64";
-          cpp_type = "uint64_t";
-          break;
-        case FieldDescriptor::TYPE_FLOAT:
-          getter = "as_float";
-          cpp_type = "float";
-          break;
-        case FieldDescriptor::TYPE_DOUBLE:
-          getter = "as_double";
-          cpp_type = "double";
-          break;
-        case FieldDescriptor::TYPE_ENUM:
-          getter = "as_int32";
-          cpp_type = "int32_t";
-          break;
-        case FieldDescriptor::TYPE_STRING:
-          getter = "as_string";
-          cpp_type = "::protozero::ConstChars";
-          break;
-        case FieldDescriptor::TYPE_MESSAGE:
-        case FieldDescriptor::TYPE_BYTES:
-          getter = "as_bytes";
-          cpp_type = "::protozero::ConstBytes";
-          break;
-        case FieldDescriptor::TYPE_GROUP:
-          continue;
-      }
-
-      stub_h_->Print("bool has_$name$() const { return at<$id$>().valid(); }\n",
-                     "name", field->name(), "id",
-                     std::to_string(field->number()));
-
-      if (field->is_repeated()) {
-        stub_h_->Print(
-            "::protozero::RepeatedFieldIterator $name$() const { return "
-            "GetRepeated($id$); }\n",
-            "name", field->name(), "id", std::to_string(field->number()));
-      } else {
-        stub_h_->Print(
-            "$cpp_type$ $name$() const { return at<$id$>().$getter$(); }\n",
-            "name", field->name(), "id", std::to_string(field->number()),
-            "cpp_type", cpp_type, "getter", getter);
-      }
-    }
-    stub_h_->Outdent();
-    stub_h_->Print("};\n\n");
-  }
-
-  void GenerateConstantsForMessageFields(const Descriptor* message) {
-    const bool has_fields = (message->field_count() > 0);
-
-    // Field number constants.
-    if (has_fields) {
-      stub_h_->Print("enum : int32_t {\n");
-      stub_h_->Indent();
-
-      for (int i = 0; i < message->field_count(); ++i) {
-        const FieldDescriptor* field = message->field(i);
-        stub_h_->Print("$name$ = $id$,\n", "name",
-                       GetFieldNumberConstant(field), "id",
-                       std::to_string(field->number()));
-      }
-      stub_h_->Outdent();
-      stub_h_->Print("};\n");
-    }
-  }
-
-  void GenerateMessageDescriptor(const Descriptor* message) {
-    GenerateDecoder(message);
-
-    stub_h_->Print(
-        "class $name$ : public ::protozero::Message {\n"
-        " public:\n",
-        "name", GetCppClassName(message));
-    stub_h_->Indent();
-
-    stub_h_->Print("using Decoder = $name$_Decoder;\n", "name",
-                   GetCppClassName(message));
-
-    GenerateConstantsForMessageFields(message);
-
-    // Using statements for nested messages.
-    for (int i = 0; i < message->nested_type_count(); ++i) {
-      const Descriptor* nested_message = message->nested_type(i);
-      stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
-                     nested_message->name(), "global_name",
-                     GetCppClassName(nested_message, true));
-    }
-
-    // Using statements for nested enums.
-    for (int i = 0; i < message->enum_type_count(); ++i) {
-      const EnumDescriptor* nested_enum = message->enum_type(i);
-      stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
-                     nested_enum->name(), "global_name",
-                     GetCppClassName(nested_enum, true));
-    }
-
-    // Values of nested enums.
-    for (int i = 0; i < message->enum_type_count(); ++i) {
-      const EnumDescriptor* nested_enum = message->enum_type(i);
-      std::string value_name_prefix = GetCppClassName(nested_enum) + "_";
-
-      for (int j = 0; j < nested_enum->value_count(); ++j) {
-        const EnumValueDescriptor* value = nested_enum->value(j);
-        stub_h_->Print("static const $class$ $name$ = $full_name$;\n", "class",
-                       nested_enum->name(), "name", value->name(), "full_name",
-                       value_name_prefix + value->name());
-      }
-    }
-
-    // Field descriptors.
-    for (int i = 0; i < message->field_count(); ++i) {
-      const FieldDescriptor* field = message->field(i);
-      if (field->is_packed()) {
-        Abort("Packed repeated fields are not supported.");
-        return;
-      }
-      if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
-        GenerateSimpleFieldDescriptor(field);
-      } else {
-        GenerateNestedMessageFieldDescriptor(field);
-      }
-    }
-
-    stub_h_->Outdent();
-    stub_h_->Print("};\n\n");
-  }
-
-  void GenerateEpilogue() {
-    for (unsigned i = 0; i < namespaces_.size(); ++i) {
-      stub_h_->Print("} // Namespace.\n");
-    }
-    stub_h_->Print("#endif  // Include guard.\n");
-  }
-
-  const FileDescriptor* const source_;
-  Printer* const stub_h_;
-  std::string error_;
-
-  std::string package_;
-  std::string wrapper_namespace_;
-  std::vector<std::string> namespaces_;
-  std::string full_namespace_prefix_;
-  std::vector<const Descriptor*> messages_;
-  std::vector<const EnumDescriptor*> enums_;
-
-  // The custom *Comp comparators are to ensure determinism of the generator.
-  std::set<const FileDescriptor*, FileDescriptorComp> public_imports_;
-  std::set<const FileDescriptor*, FileDescriptorComp> private_imports_;
-  std::set<const Descriptor*, DescriptorComp> referenced_messages_;
-  std::set<const EnumDescriptor*, EnumDescriptorComp> referenced_enums_;
-};
-
-}  // namespace
-
-ProtoZeroGenerator::ProtoZeroGenerator() {}
-
-ProtoZeroGenerator::~ProtoZeroGenerator() {}
-
-bool ProtoZeroGenerator::Generate(const FileDescriptor* file,
-                                  const std::string& options,
-                                  GeneratorContext* context,
-                                  std::string* error) const {
-  const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
-      context->Open(ProtoStubName(file) + ".h"));
-  const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream(
-      context->Open(ProtoStubName(file) + ".cc"));
-
-  // Variables are delimited by $.
-  Printer stub_h_printer(stub_h_file_stream.get(), '$');
-  GeneratorJob job(file, &stub_h_printer);
-
-  Printer stub_cc_printer(stub_cc_file_stream.get(), '$');
-  stub_cc_printer.Print("// Intentionally empty\n");
-
-  // Parse additional options.
-  for (const std::string& option : Split(options, ",")) {
-    std::vector<std::string> option_pair = Split(option, "=");
-    job.SetOption(option_pair[0], option_pair[1]);
-  }
-
-  if (!job.GenerateStubs()) {
-    *error = job.GetFirstError();
-    return false;
-  }
-  return true;
-}
-
-}  // namespace protozero
diff --git a/src/protozero/protoc_plugin/protozero_generator.h b/src/protozero/protoc_plugin/protozero_generator.h
deleted file mode 100644
index 9b40e46..0000000
--- a/src/protozero/protoc_plugin/protozero_generator.h
+++ /dev/null
@@ -1,40 +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.
- */
-
-#ifndef SRC_PROTOZERO_PROTOC_PLUGIN_PROTOZERO_GENERATOR_H_
-#define SRC_PROTOZERO_PROTOC_PLUGIN_PROTOZERO_GENERATOR_H_
-
-#include <string>
-
-#include <google/protobuf/compiler/code_generator.h>
-
-namespace protozero {
-
-class ProtoZeroGenerator : public ::google::protobuf::compiler::CodeGenerator {
- public:
-  explicit ProtoZeroGenerator();
-  ~ProtoZeroGenerator() override;
-
-  // CodeGenerator implementation
-  bool Generate(const google::protobuf::FileDescriptor* file,
-                const std::string& options,
-                google::protobuf::compiler::GeneratorContext* context,
-                std::string* error) const override;
-};
-
-}  // namespace protozero
-
-#endif  // SRC_PROTOZERO_PROTOC_PLUGIN_PROTOZERO_GENERATOR_H_
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index 611da0f..8902b6b 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -14,8 +14,741 @@
  * limitations under the License.
  */
 
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include <google/protobuf/compiler/code_generator.h>
 #include <google/protobuf/compiler/plugin.h>
-#include "src/protozero/protoc_plugin/protozero_generator.h"
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace protozero {
+namespace {
+
+using google::protobuf::Descriptor;
+using google::protobuf::EnumDescriptor;
+using google::protobuf::EnumValueDescriptor;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::FileDescriptor;
+using google::protobuf::Split;
+using google::protobuf::StripPrefixString;
+using google::protobuf::StripString;
+using google::protobuf::StripSuffixString;
+using google::protobuf::UpperString;
+using google::protobuf::compiler::GeneratorContext;
+using google::protobuf::io::Printer;
+using google::protobuf::io::ZeroCopyOutputStream;
+
+// Keep this value in sync with ProtoDecoder::kMaxDecoderFieldId. If they go out
+// of sync pbzero.h files will stop compiling, hitting the at() static_assert.
+// Not worth an extra dependency.
+constexpr int kMaxDecoderFieldId = 999;
+
+void Assert(bool condition) {
+  if (!condition)
+    __builtin_trap();
+}
+
+struct FileDescriptorComp {
+  bool operator()(const FileDescriptor* lhs, const FileDescriptor* rhs) const {
+    int comp = lhs->name().compare(rhs->name());
+    Assert(comp != 0 || lhs == rhs);
+    return comp < 0;
+  }
+};
+
+struct DescriptorComp {
+  bool operator()(const Descriptor* lhs, const Descriptor* rhs) const {
+    int comp = lhs->full_name().compare(rhs->full_name());
+    Assert(comp != 0 || lhs == rhs);
+    return comp < 0;
+  }
+};
+
+struct EnumDescriptorComp {
+  bool operator()(const EnumDescriptor* lhs, const EnumDescriptor* rhs) const {
+    int comp = lhs->full_name().compare(rhs->full_name());
+    Assert(comp != 0 || lhs == rhs);
+    return comp < 0;
+  }
+};
+
+inline std::string ProtoStubName(const FileDescriptor* proto) {
+  return StripSuffixString(proto->name(), ".proto") + ".pbzero";
+}
+
+class GeneratorJob {
+ public:
+  GeneratorJob(const FileDescriptor* file, Printer* stub_h_printer)
+      : source_(file), stub_h_(stub_h_printer) {}
+
+  bool GenerateStubs() {
+    Preprocess();
+    GeneratePrologue();
+    for (const EnumDescriptor* enumeration : enums_)
+      GenerateEnumDescriptor(enumeration);
+    for (const Descriptor* message : messages_)
+      GenerateMessageDescriptor(message);
+    GenerateEpilogue();
+    return error_.empty();
+  }
+
+  void SetOption(const std::string& name, const std::string& value) {
+    if (name == "wrapper_namespace") {
+      wrapper_namespace_ = value;
+    } else {
+      Abort(std::string() + "Unknown plugin option '" + name + "'.");
+    }
+  }
+
+  // If generator fails to produce stubs for a particular proto definitions
+  // it finishes with undefined output and writes the first error occured.
+  const std::string& GetFirstError() const { return error_; }
+
+ private:
+  // Only the first error will be recorded.
+  void Abort(const std::string& reason) {
+    if (error_.empty())
+      error_ = reason;
+  }
+
+  // Get full name (including outer descriptors) of proto descriptor.
+  template <class T>
+  inline std::string GetDescriptorName(const T* descriptor) {
+    if (!package_.empty()) {
+      return StripPrefixString(descriptor->full_name(), package_ + ".");
+    } else {
+      return descriptor->full_name();
+    }
+  }
+
+  // Get C++ class name corresponding to proto descriptor.
+  // Nested names are splitted by underscores. Underscores in type names aren't
+  // prohibited but not recommended in order to avoid name collisions.
+  template <class T>
+  inline std::string GetCppClassName(const T* descriptor, bool full = false) {
+    std::string name = GetDescriptorName(descriptor);
+    StripString(&name, ".", '_');
+    if (full)
+      name = full_namespace_prefix_ + name;
+    return name;
+  }
+
+  inline std::string GetFieldNumberConstant(const FieldDescriptor* field) {
+    std::string name = field->camelcase_name();
+    if (!name.empty()) {
+      name.at(0) = static_cast<char>(toupper(name.at(0)));
+      name = "k" + name + "FieldNumber";
+    } else {
+      // Protoc allows fields like 'bool _ = 1'.
+      Abort("Empty field name in camel case notation.");
+    }
+    return name;
+  }
+
+  // Small enums can be written faster without involving VarInt encoder.
+  inline bool IsTinyEnumField(const FieldDescriptor* field) {
+    if (field->type() != FieldDescriptor::TYPE_ENUM)
+      return false;
+    const EnumDescriptor* enumeration = field->enum_type();
+
+    for (int i = 0; i < enumeration->value_count(); ++i) {
+      int32_t value = enumeration->value(i)->number();
+      if (value < 0 || value > 0x7F)
+        return false;
+    }
+    return true;
+  }
+
+  void CollectDescriptors() {
+    // Collect message descriptors in DFS order.
+    std::vector<const Descriptor*> stack;
+    for (int i = 0; i < source_->message_type_count(); ++i)
+      stack.push_back(source_->message_type(i));
+
+    while (!stack.empty()) {
+      const Descriptor* message = stack.back();
+      stack.pop_back();
+      messages_.push_back(message);
+      for (int i = 0; i < message->nested_type_count(); ++i) {
+        stack.push_back(message->nested_type(i));
+      }
+    }
+
+    // Collect enums.
+    for (int i = 0; i < source_->enum_type_count(); ++i)
+      enums_.push_back(source_->enum_type(i));
+
+    for (const Descriptor* message : messages_) {
+      for (int i = 0; i < message->enum_type_count(); ++i) {
+        enums_.push_back(message->enum_type(i));
+      }
+    }
+  }
+
+  void CollectDependencies() {
+    // Public import basically means that callers only need to import this
+    // proto in order to use the stuff publicly imported by this proto.
+    for (int i = 0; i < source_->public_dependency_count(); ++i)
+      public_imports_.insert(source_->public_dependency(i));
+
+    if (source_->weak_dependency_count() > 0)
+      Abort("Weak imports are not supported.");
+
+    // Sanity check. Collect public imports (of collected imports) in DFS order.
+    // Visibilty for current proto:
+    // - all imports listed in current proto,
+    // - public imports of everything imported (recursive).
+    std::vector<const FileDescriptor*> stack;
+    for (int i = 0; i < source_->dependency_count(); ++i) {
+      const FileDescriptor* import = source_->dependency(i);
+      stack.push_back(import);
+      if (public_imports_.count(import) == 0) {
+        private_imports_.insert(import);
+      }
+    }
+
+    while (!stack.empty()) {
+      const FileDescriptor* import = stack.back();
+      stack.pop_back();
+      // Having imports under different packages leads to unnecessary
+      // complexity with namespaces.
+      if (import->package() != package_)
+        Abort("Imported proto must be in the same package.");
+
+      for (int i = 0; i < import->public_dependency_count(); ++i) {
+        stack.push_back(import->public_dependency(i));
+      }
+    }
+
+    // Collect descriptors of messages and enums used in current proto.
+    // It will be used to generate necessary forward declarations and performed
+    // sanity check guarantees that everything lays in the same namespace.
+    for (const Descriptor* message : messages_) {
+      for (int i = 0; i < message->field_count(); ++i) {
+        const FieldDescriptor* field = message->field(i);
+
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
+          if (public_imports_.count(field->message_type()->file()) == 0) {
+            // Avoid multiple forward declarations since
+            // public imports have been already included.
+            referenced_messages_.insert(field->message_type());
+          }
+        } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
+          if (public_imports_.count(field->enum_type()->file()) == 0) {
+            referenced_enums_.insert(field->enum_type());
+          }
+        }
+      }
+    }
+  }
+
+  void Preprocess() {
+    // Package name maps to a series of namespaces.
+    package_ = source_->package();
+    namespaces_ = Split(package_, ".");
+    if (!wrapper_namespace_.empty())
+      namespaces_.push_back(wrapper_namespace_);
+
+    full_namespace_prefix_ = "::";
+    for (const std::string& ns : namespaces_)
+      full_namespace_prefix_ += ns + "::";
+
+    CollectDescriptors();
+    CollectDependencies();
+  }
+
+  // Print top header, namespaces and forward declarations.
+  void GeneratePrologue() {
+    std::string greeting =
+        "// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.\n";
+    std::string guard = package_ + "_" + source_->name() + "_H_";
+    UpperString(&guard);
+    StripString(&guard, ".-/\\", '_');
+
+    stub_h_->Print(
+        "$greeting$\n"
+        "#ifndef $guard$\n"
+        "#define $guard$\n\n"
+        "#include <stddef.h>\n"
+        "#include <stdint.h>\n\n"
+        "#include \"perfetto/protozero/proto_decoder.h\"\n"
+        "#include \"perfetto/protozero/message.h\"\n",
+        "greeting", greeting, "guard", guard);
+
+    // Print includes for public imports.
+    for (const FileDescriptor* dependency : public_imports_) {
+      // Dependency name could contain slashes but importing from upper-level
+      // directories is not possible anyway since build system processes each
+      // proto file individually. Hence proto lookup path is always equal to the
+      // directory where particular proto file is located and protoc does not
+      // allow reference to upper directory (aka ..) in import path.
+      //
+      // Laconically said:
+      // - source_->name() may never have slashes,
+      // - dependency->name() may have slashes but always refers to inner path.
+      stub_h_->Print("#include \"$name$.h\"\n", "name",
+                     ProtoStubName(dependency));
+    }
+    stub_h_->Print("\n");
+
+    // Print namespaces.
+    for (const std::string& ns : namespaces_) {
+      stub_h_->Print("namespace $ns$ {\n", "ns", ns);
+    }
+    stub_h_->Print("\n");
+
+    // Print forward declarations.
+    for (const Descriptor* message : referenced_messages_) {
+      stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
+    }
+    for (const EnumDescriptor* enumeration : referenced_enums_) {
+      stub_h_->Print("enum $class$ : int32_t;\n", "class",
+                     GetCppClassName(enumeration));
+    }
+    stub_h_->Print("\n");
+  }
+
+  void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
+    stub_h_->Print("enum $class$ : int32_t {\n", "class",
+                   GetCppClassName(enumeration));
+    stub_h_->Indent();
+
+    std::string value_name_prefix;
+    if (enumeration->containing_type() != nullptr)
+      value_name_prefix = GetCppClassName(enumeration) + "_";
+
+    std::string min_name, max_name;
+    int min_val = std::numeric_limits<int>::max();
+    int max_val = -1;
+    for (int i = 0; i < enumeration->value_count(); ++i) {
+      const EnumValueDescriptor* value = enumeration->value(i);
+      stub_h_->Print("$name$ = $number$,\n", "name",
+                     value_name_prefix + value->name(), "number",
+                     std::to_string(value->number()));
+      if (value->number() < min_val) {
+        min_val = value->number();
+        min_name = value_name_prefix + value->name();
+      }
+      if (value->number() > max_val) {
+        max_val = value->number();
+        max_name = value_name_prefix + value->name();
+      }
+    }
+    stub_h_->Outdent();
+    stub_h_->Print("};\n\n");
+    stub_h_->Print("const $class$ $class$_MIN = $min$;\n", "class",
+                   GetCppClassName(enumeration), "min", min_name);
+    stub_h_->Print("const $class$ $class$_MAX = $max$;\n", "class",
+                   GetCppClassName(enumeration), "max", max_name);
+    stub_h_->Print("\n");
+  }
+
+  void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
+    std::map<std::string, std::string> setter;
+    setter["id"] = std::to_string(field->number());
+    setter["name"] = field->name();
+    setter["action"] = field->is_repeated() ? "add" : "set";
+
+    std::string appender;
+    std::string cpp_type;
+
+    switch (field->type()) {
+      case FieldDescriptor::TYPE_BOOL: {
+        appender = "AppendTinyVarInt";
+        cpp_type = "bool";
+        break;
+      }
+      case FieldDescriptor::TYPE_INT32: {
+        appender = "AppendVarInt";
+        cpp_type = "int32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_INT64: {
+        appender = "AppendVarInt";
+        cpp_type = "int64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_UINT32: {
+        appender = "AppendVarInt";
+        cpp_type = "uint32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_UINT64: {
+        appender = "AppendVarInt";
+        cpp_type = "uint64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SINT32: {
+        appender = "AppendSignedVarInt";
+        cpp_type = "int32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SINT64: {
+        appender = "AppendSignedVarInt";
+        cpp_type = "int64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_FIXED32: {
+        appender = "AppendFixed";
+        cpp_type = "uint32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_FIXED64: {
+        appender = "AppendFixed";
+        cpp_type = "uint64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SFIXED32: {
+        appender = "AppendFixed";
+        cpp_type = "int32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SFIXED64: {
+        appender = "AppendFixed";
+        cpp_type = "int64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_FLOAT: {
+        appender = "AppendFixed";
+        cpp_type = "float";
+        break;
+      }
+      case FieldDescriptor::TYPE_DOUBLE: {
+        appender = "AppendFixed";
+        cpp_type = "double";
+        break;
+      }
+      case FieldDescriptor::TYPE_ENUM: {
+        appender = IsTinyEnumField(field) ? "AppendTinyVarInt" : "AppendVarInt";
+        cpp_type = GetCppClassName(field->enum_type(), true);
+        break;
+      }
+      case FieldDescriptor::TYPE_STRING: {
+        appender = "AppendString";
+        cpp_type = "const char*";
+        break;
+      }
+      case FieldDescriptor::TYPE_BYTES: {
+        stub_h_->Print(
+            setter,
+            "void $action$_$name$(const uint8_t* data, size_t size) {\n"
+            "  AppendBytes($id$, data, size);\n"
+            "}\n");
+        return;
+      }
+      case FieldDescriptor::TYPE_GROUP:
+      case FieldDescriptor::TYPE_MESSAGE: {
+        Abort("Unsupported field type.");
+        return;
+      }
+    }
+    setter["appender"] = appender;
+    setter["cpp_type"] = cpp_type;
+    stub_h_->Print(setter,
+                   "void $action$_$name$($cpp_type$ value) {\n"
+                   "  $appender$($id$, value);\n"
+                   "}\n");
+
+    // For strings also generate a variant for non-null terminated strings.
+    if (field->type() == FieldDescriptor::TYPE_STRING) {
+      stub_h_->Print(setter,
+                     "// Doesn't check for null terminator.\n"
+                     "// Expects |value| to be at least |size| long.\n"
+                     "void $action$_$name$($cpp_type$ value, size_t size) {\n"
+                     "  AppendBytes($id$, value, size);\n"
+                     "}\n");
+    }
+  }
+
+  void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
+    std::string action = field->is_repeated() ? "add" : "set";
+    std::string inner_class = GetCppClassName(field->message_type());
+    stub_h_->Print(
+        "template <typename T = $inner_class$> T* $action$_$name$() {\n"
+        "  return BeginNestedMessage<T>($id$);\n"
+        "}\n\n",
+        "id", std::to_string(field->number()), "name", field->name(), "action",
+        action, "inner_class", inner_class);
+  }
+
+  void GenerateDecoder(const Descriptor* message) {
+    int max_field_id = 0;
+    bool has_repeated_fields = false;
+    for (int i = 0; i < message->field_count(); ++i) {
+      const FieldDescriptor* field = message->field(i);
+      if (field->number() > kMaxDecoderFieldId)
+        continue;
+      max_field_id = std::max(max_field_id, field->number());
+      if (field->is_repeated())
+        has_repeated_fields = true;
+    }
+
+    std::string class_name = GetCppClassName(message) + "_Decoder";
+    stub_h_->Print(
+        "class $name$ : public "
+        "::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/$max$, "
+        "/*HAS_REPEATED_FIELDS=*/$rep$> {\n",
+        "name", class_name, "max", std::to_string(max_field_id), "rep",
+        has_repeated_fields ? "true" : "false");
+    stub_h_->Print(" public:\n");
+    stub_h_->Indent();
+    stub_h_->Print(
+        "$name$(const uint8_t* data, size_t len) "
+        ": TypedProtoDecoder(data, len) {}\n",
+        "name", class_name);
+    stub_h_->Print(
+        "explicit $name$(const std::string& raw) : "
+        "TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), "
+        "raw.size()) {}\n",
+        "name", class_name);
+    stub_h_->Print(
+        "explicit $name$(const ::protozero::ConstBytes& raw) : "
+        "TypedProtoDecoder(raw.data, raw.size) {}\n",
+        "name", class_name);
+
+    for (int i = 0; i < message->field_count(); ++i) {
+      const FieldDescriptor* field = message->field(i);
+      if (field->is_packed()) {
+        Abort("Packed repeated fields are not supported.");
+        return;
+      }
+
+      if (field->number() > max_field_id) {
+        stub_h_->Print("// field $name$ omitted because its id is too high\n",
+                       "name", field->name());
+        continue;
+      }
+      std::string getter;
+      std::string cpp_type;
+      switch (field->type()) {
+        case FieldDescriptor::TYPE_BOOL:
+          getter = "as_bool";
+          cpp_type = "bool";
+          break;
+        case FieldDescriptor::TYPE_SFIXED32:
+        case FieldDescriptor::TYPE_SINT32:
+        case FieldDescriptor::TYPE_INT32:
+          getter = "as_int32";
+          cpp_type = "int32_t";
+          break;
+        case FieldDescriptor::TYPE_SFIXED64:
+        case FieldDescriptor::TYPE_SINT64:
+        case FieldDescriptor::TYPE_INT64:
+          getter = "as_int64";
+          cpp_type = "int64_t";
+          break;
+        case FieldDescriptor::TYPE_FIXED32:
+        case FieldDescriptor::TYPE_UINT32:
+          getter = "as_uint32";
+          cpp_type = "uint32_t";
+          break;
+        case FieldDescriptor::TYPE_FIXED64:
+        case FieldDescriptor::TYPE_UINT64:
+          getter = "as_uint64";
+          cpp_type = "uint64_t";
+          break;
+        case FieldDescriptor::TYPE_FLOAT:
+          getter = "as_float";
+          cpp_type = "float";
+          break;
+        case FieldDescriptor::TYPE_DOUBLE:
+          getter = "as_double";
+          cpp_type = "double";
+          break;
+        case FieldDescriptor::TYPE_ENUM:
+          getter = "as_int32";
+          cpp_type = "int32_t";
+          break;
+        case FieldDescriptor::TYPE_STRING:
+          getter = "as_string";
+          cpp_type = "::protozero::ConstChars";
+          break;
+        case FieldDescriptor::TYPE_MESSAGE:
+        case FieldDescriptor::TYPE_BYTES:
+          getter = "as_bytes";
+          cpp_type = "::protozero::ConstBytes";
+          break;
+        case FieldDescriptor::TYPE_GROUP:
+          continue;
+      }
+
+      stub_h_->Print("bool has_$name$() const { return at<$id$>().valid(); }\n",
+                     "name", field->name(), "id",
+                     std::to_string(field->number()));
+
+      if (field->is_repeated()) {
+        stub_h_->Print(
+            "::protozero::RepeatedFieldIterator $name$() const { return "
+            "GetRepeated($id$); }\n",
+            "name", field->name(), "id", std::to_string(field->number()));
+      } else {
+        stub_h_->Print(
+            "$cpp_type$ $name$() const { return at<$id$>().$getter$(); }\n",
+            "name", field->name(), "id", std::to_string(field->number()),
+            "cpp_type", cpp_type, "getter", getter);
+      }
+    }
+    stub_h_->Outdent();
+    stub_h_->Print("};\n\n");
+  }
+
+  void GenerateConstantsForMessageFields(const Descriptor* message) {
+    const bool has_fields = (message->field_count() > 0);
+
+    // Field number constants.
+    if (has_fields) {
+      stub_h_->Print("enum : int32_t {\n");
+      stub_h_->Indent();
+
+      for (int i = 0; i < message->field_count(); ++i) {
+        const FieldDescriptor* field = message->field(i);
+        stub_h_->Print("$name$ = $id$,\n", "name",
+                       GetFieldNumberConstant(field), "id",
+                       std::to_string(field->number()));
+      }
+      stub_h_->Outdent();
+      stub_h_->Print("};\n");
+    }
+  }
+
+  void GenerateMessageDescriptor(const Descriptor* message) {
+    GenerateDecoder(message);
+
+    stub_h_->Print(
+        "class $name$ : public ::protozero::Message {\n"
+        " public:\n",
+        "name", GetCppClassName(message));
+    stub_h_->Indent();
+
+    stub_h_->Print("using Decoder = $name$_Decoder;\n", "name",
+                   GetCppClassName(message));
+
+    GenerateConstantsForMessageFields(message);
+
+    // Using statements for nested messages.
+    for (int i = 0; i < message->nested_type_count(); ++i) {
+      const Descriptor* nested_message = message->nested_type(i);
+      stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
+                     nested_message->name(), "global_name",
+                     GetCppClassName(nested_message, true));
+    }
+
+    // Using statements for nested enums.
+    for (int i = 0; i < message->enum_type_count(); ++i) {
+      const EnumDescriptor* nested_enum = message->enum_type(i);
+      stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
+                     nested_enum->name(), "global_name",
+                     GetCppClassName(nested_enum, true));
+    }
+
+    // Values of nested enums.
+    for (int i = 0; i < message->enum_type_count(); ++i) {
+      const EnumDescriptor* nested_enum = message->enum_type(i);
+      std::string value_name_prefix = GetCppClassName(nested_enum) + "_";
+
+      for (int j = 0; j < nested_enum->value_count(); ++j) {
+        const EnumValueDescriptor* value = nested_enum->value(j);
+        stub_h_->Print("static const $class$ $name$ = $full_name$;\n", "class",
+                       nested_enum->name(), "name", value->name(), "full_name",
+                       value_name_prefix + value->name());
+      }
+    }
+
+    // Field descriptors.
+    for (int i = 0; i < message->field_count(); ++i) {
+      const FieldDescriptor* field = message->field(i);
+      if (field->is_packed()) {
+        Abort("Packed repeated fields are not supported.");
+        return;
+      }
+      if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+        GenerateSimpleFieldDescriptor(field);
+      } else {
+        GenerateNestedMessageFieldDescriptor(field);
+      }
+    }
+
+    stub_h_->Outdent();
+    stub_h_->Print("};\n\n");
+  }
+
+  void GenerateEpilogue() {
+    for (unsigned i = 0; i < namespaces_.size(); ++i) {
+      stub_h_->Print("} // Namespace.\n");
+    }
+    stub_h_->Print("#endif  // Include guard.\n");
+  }
+
+  const FileDescriptor* const source_;
+  Printer* const stub_h_;
+  std::string error_;
+
+  std::string package_;
+  std::string wrapper_namespace_;
+  std::vector<std::string> namespaces_;
+  std::string full_namespace_prefix_;
+  std::vector<const Descriptor*> messages_;
+  std::vector<const EnumDescriptor*> enums_;
+
+  // The custom *Comp comparators are to ensure determinism of the generator.
+  std::set<const FileDescriptor*, FileDescriptorComp> public_imports_;
+  std::set<const FileDescriptor*, FileDescriptorComp> private_imports_;
+  std::set<const Descriptor*, DescriptorComp> referenced_messages_;
+  std::set<const EnumDescriptor*, EnumDescriptorComp> referenced_enums_;
+};
+
+class ProtoZeroGenerator : public ::google::protobuf::compiler::CodeGenerator {
+ public:
+  explicit ProtoZeroGenerator();
+  ~ProtoZeroGenerator() override;
+
+  // CodeGenerator implementation
+  bool Generate(const google::protobuf::FileDescriptor* file,
+                const std::string& options,
+                GeneratorContext* context,
+                std::string* error) const override;
+};
+
+ProtoZeroGenerator::ProtoZeroGenerator() {}
+
+ProtoZeroGenerator::~ProtoZeroGenerator() {}
+
+bool ProtoZeroGenerator::Generate(const FileDescriptor* file,
+                                  const std::string& options,
+                                  GeneratorContext* context,
+                                  std::string* error) const {
+  const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
+      context->Open(ProtoStubName(file) + ".h"));
+  const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream(
+      context->Open(ProtoStubName(file) + ".cc"));
+
+  // Variables are delimited by $.
+  Printer stub_h_printer(stub_h_file_stream.get(), '$');
+  GeneratorJob job(file, &stub_h_printer);
+
+  Printer stub_cc_printer(stub_cc_file_stream.get(), '$');
+  stub_cc_printer.Print("// Intentionally empty\n");
+
+  // Parse additional options.
+  for (const std::string& option : Split(options, ",")) {
+    std::vector<std::string> option_pair = Split(option, "=");
+    job.SetOption(option_pair[0], option_pair[1]);
+  }
+
+  if (!job.GenerateStubs()) {
+    *error = job.GetFirstError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+}  // namespace protozero
 
 int main(int argc, char* argv[]) {
   ::protozero::ProtoZeroGenerator generator;
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 02143bc..183a914 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -519,7 +519,7 @@
         # TODO(skyostil): Can we detect this some other way?
         if plugin == 'ipc_plugin':
             namespaces.append('ipc')
-        elif plugin == 'protoc_plugin':
+        elif plugin == 'protozero_plugin':
             namespaces = ['pbzero']
         for dep in target['deps']:
             if desc[dep]['type'] != 'executable':