Merge "Add name hash to chrome user action proto."
diff --git a/.gitignore b/.gitignore
index 655f094..f24c442 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 .android_config
 .ccls-cache
+.clangd
 .cproject
 .deps_sha1
 .DS_Store
diff --git a/Android.bp b/Android.bp
index 5ed362b..c1ca304 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,6 +21,7 @@
     "src/trace_processor/metrics/android/android_batt.sql",
     "src/trace_processor/metrics/android/android_cpu.sql",
     "src/trace_processor/metrics/android/android_cpu_agg.sql",
+    "src/trace_processor/metrics/android/android_hwui_metric.sql",
     "src/trace_processor/metrics/android/android_ion.sql",
     "src/trace_processor/metrics/android/android_lmk.sql",
     "src/trace_processor/metrics/android/android_lmk_reason.sql",
@@ -107,7 +108,8 @@
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
     ":perfetto_src_profiling_memory_daemon",
     ":perfetto_src_profiling_memory_proc_utils",
     ":perfetto_src_profiling_memory_ring_buffer",
@@ -115,8 +117,9 @@
     ":perfetto_src_profiling_memory_wire_protocol",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_producer_producer",
     "src/profiling/memory/main.cc",
   ],
   shared_libs: [
@@ -296,7 +299,9 @@
     ":perfetto_src_android_internal_lazy_library_loader",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
+    ":perfetto_src_ipc_host",
     ":perfetto_src_perfetto_cmd_perfetto_atoms",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_traced_probes_android_log_android_log",
@@ -313,9 +318,13 @@
     ":perfetto_src_traced_probes_sys_stats_sys_stats",
     ":perfetto_src_traced_service_service",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_consumer_api_deprecated",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_consumer_api_deprecated_consumer_api_deprecated",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
+    ":perfetto_src_tracing_ipc_service_service",
   ],
   shared_libs: [
     "liblog",
@@ -465,13 +474,22 @@
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
+    ":perfetto_src_ipc_host",
     ":perfetto_src_protozero_protozero",
-    ":perfetto_src_tracing_client_api",
+    ":perfetto_src_tracing_client_api_base",
+    ":perfetto_src_tracing_client_api_system_backend_only",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_in_process_backend_fake",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
+    ":perfetto_src_tracing_ipc_service_service",
     ":perfetto_src_tracing_platform_posix",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_system_process_backend",
   ],
   export_include_dirs: [
     "include",
@@ -619,15 +637,18 @@
     ":perfetto_src_android_internal_lazy_library_loader",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
     ":perfetto_src_perfetto_cmd_perfetto_atoms",
     ":perfetto_src_perfetto_cmd_perfetto_cmd",
     ":perfetto_src_perfetto_cmd_protos_gen",
     ":perfetto_src_perfetto_cmd_trigger_producer",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
     "src/perfetto_cmd/main.cc",
   ],
   shared_libs: [
@@ -755,7 +776,9 @@
     ":perfetto_src_base_base",
     ":perfetto_src_base_test_support",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
+    ":perfetto_src_ipc_host",
     ":perfetto_src_perfetto_cmd_perfetto_atoms",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_traced_probes_android_log_android_log",
@@ -770,10 +793,13 @@
     ":perfetto_src_traced_probes_ps_ps",
     ":perfetto_src_traced_probes_sys_stats_sys_stats",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
-    ":perfetto_test_task_runner_thread",
-    ":perfetto_test_task_runner_thread_delegates",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
+    ":perfetto_src_tracing_ipc_service_service",
+    ":perfetto_test_end_to_end_integrationtests",
     ":perfetto_test_test_helper",
     "test/cts/device_feature_test_cts.cc",
     "test/cts/end_to_end_integrationtest_cts.cc",
@@ -905,6 +931,221 @@
   ],
 }
 
+// GN: //test/cts:perfetto_cts_jni_deps
+cc_library_static {
+  name: "perfetto_cts_jni_deps",
+  srcs: [
+    ":perfetto_include_perfetto_base_base",
+    ":perfetto_include_perfetto_ext_base_base",
+    ":perfetto_include_perfetto_ext_ipc_ipc",
+    ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
+    ":perfetto_include_perfetto_ext_traced_traced",
+    ":perfetto_include_perfetto_ext_tracing_core_core",
+    ":perfetto_include_perfetto_ext_tracing_ipc_ipc",
+    ":perfetto_include_perfetto_protozero_protozero",
+    ":perfetto_include_perfetto_tracing_core_core",
+    ":perfetto_include_perfetto_tracing_core_forward_decls",
+    ":perfetto_include_perfetto_tracing_tracing",
+    ":perfetto_protos_perfetto_common_cpp_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_android_cpp_gen",
+    ":perfetto_protos_perfetto_config_android_zero_gen",
+    ":perfetto_protos_perfetto_config_cpp_gen",
+    ":perfetto_protos_perfetto_config_ftrace_cpp_gen",
+    ":perfetto_protos_perfetto_config_ftrace_zero_gen",
+    ":perfetto_protos_perfetto_config_gpu_cpp_gen",
+    ":perfetto_protos_perfetto_config_gpu_zero_gen",
+    ":perfetto_protos_perfetto_config_inode_file_cpp_gen",
+    ":perfetto_protos_perfetto_config_inode_file_zero_gen",
+    ":perfetto_protos_perfetto_config_power_cpp_gen",
+    ":perfetto_protos_perfetto_config_power_zero_gen",
+    ":perfetto_protos_perfetto_config_process_stats_cpp_gen",
+    ":perfetto_protos_perfetto_config_process_stats_zero_gen",
+    ":perfetto_protos_perfetto_config_profiling_cpp_gen",
+    ":perfetto_protos_perfetto_config_profiling_zero_gen",
+    ":perfetto_protos_perfetto_config_sys_stats_cpp_gen",
+    ":perfetto_protos_perfetto_config_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
+    ":perfetto_protos_perfetto_ipc_cpp_gen",
+    ":perfetto_protos_perfetto_ipc_ipc_gen",
+    ":perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen",
+    ":perfetto_protos_perfetto_trace_android_cpp_gen",
+    ":perfetto_protos_perfetto_trace_android_zero_gen",
+    ":perfetto_protos_perfetto_trace_chrome_cpp_gen",
+    ":perfetto_protos_perfetto_trace_chrome_zero_gen",
+    ":perfetto_protos_perfetto_trace_filesystem_cpp_gen",
+    ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
+    ":perfetto_protos_perfetto_trace_ftrace_cpp_gen",
+    ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
+    ":perfetto_protos_perfetto_trace_gpu_cpp_gen",
+    ":perfetto_protos_perfetto_trace_gpu_zero_gen",
+    ":perfetto_protos_perfetto_trace_interned_data_cpp_gen",
+    ":perfetto_protos_perfetto_trace_interned_data_zero_gen",
+    ":perfetto_protos_perfetto_trace_minimal_cpp_gen",
+    ":perfetto_protos_perfetto_trace_minimal_zero_gen",
+    ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen",
+    ":perfetto_protos_perfetto_trace_non_minimal_zero_gen",
+    ":perfetto_protos_perfetto_trace_perfetto_cpp_gen",
+    ":perfetto_protos_perfetto_trace_perfetto_zero_gen",
+    ":perfetto_protos_perfetto_trace_power_cpp_gen",
+    ":perfetto_protos_perfetto_trace_power_zero_gen",
+    ":perfetto_protos_perfetto_trace_profiling_cpp_gen",
+    ":perfetto_protos_perfetto_trace_profiling_zero_gen",
+    ":perfetto_protos_perfetto_trace_ps_cpp_gen",
+    ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_cpp_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_trace_track_event_zero_gen",
+    ":perfetto_src_android_internal_headers",
+    ":perfetto_src_android_internal_lazy_library_loader",
+    ":perfetto_src_base_base",
+    ":perfetto_src_base_test_support",
+    ":perfetto_src_base_unix_socket",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
+    ":perfetto_src_ipc_host",
+    ":perfetto_src_perfetto_cmd_perfetto_atoms",
+    ":perfetto_src_protozero_protozero",
+    ":perfetto_src_traced_probes_android_log_android_log",
+    ":perfetto_src_traced_probes_data_source",
+    ":perfetto_src_traced_probes_filesystem_filesystem",
+    ":perfetto_src_traced_probes_ftrace_format_parser",
+    ":perfetto_src_traced_probes_ftrace_ftrace",
+    ":perfetto_src_traced_probes_metatrace_metatrace",
+    ":perfetto_src_traced_probes_packages_list_packages_list",
+    ":perfetto_src_traced_probes_power_power",
+    ":perfetto_src_traced_probes_probes_src",
+    ":perfetto_src_traced_probes_ps_ps",
+    ":perfetto_src_traced_probes_sys_stats_sys_stats",
+    ":perfetto_src_tracing_common",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
+    ":perfetto_src_tracing_ipc_service_service",
+    ":perfetto_test_test_helper",
+  ],
+  export_include_dirs: [
+    "include",
+    "include/perfetto/base/build_configs/android_tree",
+  ],
+  generated_headers: [
+    "perfetto_protos_perfetto_common_cpp_gen_headers",
+    "perfetto_protos_perfetto_common_zero_gen_headers",
+    "perfetto_protos_perfetto_config_android_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_android_zero_gen_headers",
+    "perfetto_protos_perfetto_config_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
+    "perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_gpu_zero_gen_headers",
+    "perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
+    "perfetto_protos_perfetto_config_power_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_power_zero_gen_headers",
+    "perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
+    "perfetto_protos_perfetto_config_sys_stats_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_config_zero_gen_headers",
+    "perfetto_protos_perfetto_ipc_cpp_gen_headers",
+    "perfetto_protos_perfetto_ipc_ipc_gen_headers",
+    "perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_android_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_android_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_power_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
+  ],
+  export_generated_headers: [
+    "perfetto_protos_perfetto_common_cpp_gen_headers",
+    "perfetto_protos_perfetto_common_zero_gen_headers",
+    "perfetto_protos_perfetto_config_android_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_android_zero_gen_headers",
+    "perfetto_protos_perfetto_config_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
+    "perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_gpu_zero_gen_headers",
+    "perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
+    "perfetto_protos_perfetto_config_power_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_power_zero_gen_headers",
+    "perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
+    "perfetto_protos_perfetto_config_sys_stats_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_config_zero_gen_headers",
+    "perfetto_protos_perfetto_ipc_cpp_gen_headers",
+    "perfetto_protos_perfetto_ipc_ipc_gen_headers",
+    "perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_android_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_android_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_power_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
+  ],
+  defaults: [
+    "perfetto_defaults",
+  ],
+  cflags: [
+    "-DGOOGLE_PROTOBUF_NO_RTTI",
+    "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+  ],
+}
+
 // GN: //gn:default_deps
 cc_defaults {
   name: "perfetto_defaults",
@@ -1097,7 +1338,9 @@
     ":perfetto_src_base_base",
     ":perfetto_src_base_test_support",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
+    ":perfetto_src_ipc_host",
     ":perfetto_src_perfetto_cmd_perfetto_atoms",
     ":perfetto_src_profiling_memory_client",
     ":perfetto_src_profiling_memory_daemon",
@@ -1120,16 +1363,20 @@
     ":perfetto_src_traced_probes_probes_src",
     ":perfetto_src_traced_probes_ps_ps",
     ":perfetto_src_traced_probes_sys_stats_sys_stats",
-    ":perfetto_src_tracing_client_api",
-    ":perfetto_src_tracing_client_api_integrationtests",
+    ":perfetto_src_tracing_client_api_base",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_in_process_backend",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
+    ":perfetto_src_tracing_ipc_service_service",
     ":perfetto_src_tracing_platform_posix",
+    ":perfetto_src_tracing_system_process_backend",
     ":perfetto_src_tracing_test_api_test_support",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_test_client_api_integrationtests",
     ":perfetto_test_end_to_end_integrationtests",
-    ":perfetto_test_task_runner_thread",
-    ":perfetto_test_task_runner_thread_delegates",
     ":perfetto_test_test_helper",
   ],
   shared_libs: [
@@ -1295,7 +1542,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/common/android_log_constants.pb.cc",
     "external/perfetto/protos/perfetto/common/commit_data_request.pb.cc",
@@ -1328,7 +1575,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/common/android_log_constants.pb.h",
     "external/perfetto/protos/perfetto/common/commit_data_request.pb.h",
@@ -1469,7 +1716,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/android/android_log_config.pb.cc",
     "external/perfetto/protos/perfetto/config/android/packages_list_config.pb.cc",
@@ -1486,7 +1733,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/android/android_log_config.pb.h",
     "external/perfetto/protos/perfetto/config/android/packages_list_config.pb.h",
@@ -1630,7 +1877,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.cc",
   ],
@@ -1645,7 +1892,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.h",
   ],
@@ -1741,7 +1988,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/gpu/gpu_counter_config.pb.cc",
     "external/perfetto/protos/perfetto/config/gpu/vulkan_memory_config.pb.cc",
@@ -1758,7 +2005,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/gpu/gpu_counter_config.pb.h",
     "external/perfetto/protos/perfetto/config/gpu/vulkan_memory_config.pb.h",
@@ -1854,7 +2101,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pb.cc",
   ],
@@ -1869,7 +2116,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pb.h",
   ],
@@ -1927,7 +2174,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/chrome/chrome_config.pb.cc",
     "external/perfetto/protos/perfetto/config/data_source_config.pb.cc",
@@ -1948,7 +2195,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/chrome/chrome_config.pb.h",
     "external/perfetto/protos/perfetto/config/data_source_config.pb.h",
@@ -2006,7 +2253,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/power/android_power_config.pb.cc",
   ],
@@ -2021,7 +2268,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/power/android_power_config.pb.h",
   ],
@@ -2112,7 +2359,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pb.cc",
   ],
@@ -2127,7 +2374,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pb.h",
   ],
@@ -2228,7 +2475,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/profiling/heapprofd_config.pb.cc",
     "external/perfetto/protos/perfetto/config/profiling/java_hprof_config.pb.cc",
@@ -2247,7 +2494,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/profiling/heapprofd_config.pb.h",
     "external/perfetto/protos/perfetto/config/profiling/java_hprof_config.pb.h",
@@ -2348,7 +2595,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/sys_stats/sys_stats_config.pb.cc",
   ],
@@ -2363,7 +2610,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/config/sys_stats/sys_stats_config.pb.h",
   ],
@@ -2580,6 +2827,7 @@
     "protos/perfetto/metrics/android/batt_metric.proto",
     "protos/perfetto/metrics/android/cpu_metric.proto",
     "protos/perfetto/metrics/android/heap_profile_callsites.proto",
+    "protos/perfetto/metrics/android/hwui_metric.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
     "protos/perfetto/metrics/android/java_heap_stats.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
@@ -2602,6 +2850,7 @@
     "external/perfetto/protos/perfetto/metrics/android/batt_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/heap_profile_callsites.pbzero.cc",
+    "external/perfetto/protos/perfetto/metrics/android/hwui_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/ion_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/java_heap_stats.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/lmk_metric.pbzero.cc",
@@ -2624,6 +2873,7 @@
     "protos/perfetto/metrics/android/batt_metric.proto",
     "protos/perfetto/metrics/android/cpu_metric.proto",
     "protos/perfetto/metrics/android/heap_profile_callsites.proto",
+    "protos/perfetto/metrics/android/hwui_metric.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
     "protos/perfetto/metrics/android/java_heap_stats.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
@@ -2646,6 +2896,7 @@
     "external/perfetto/protos/perfetto/metrics/android/batt_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/heap_profile_callsites.pbzero.h",
+    "external/perfetto/protos/perfetto/metrics/android/hwui_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/ion_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/java_heap_stats.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/lmk_metric.pbzero.h",
@@ -2756,7 +3007,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pb.cc",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.pb.cc",
@@ -2775,7 +3026,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pb.h",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.pb.h",
@@ -2886,7 +3137,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/chrome/chrome_benchmark_metadata.pb.cc",
     "external/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pb.cc",
@@ -2905,7 +3156,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/chrome/chrome_benchmark_metadata.pb.h",
     "external/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pb.h",
@@ -3006,7 +3257,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/filesystem/inode_file_map.pb.cc",
   ],
@@ -3021,7 +3272,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/filesystem/inode_file_map.pb.h",
   ],
@@ -3277,7 +3528,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ftrace/binder.pb.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/block.pb.cc",
@@ -3358,7 +3609,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ftrace/binder.pb.h",
     "external/perfetto/protos/perfetto/trace/ftrace/block.pb.h",
@@ -3634,7 +3885,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/gpu/gpu_counter_event.pb.cc",
     "external/perfetto/protos/perfetto/trace/gpu/gpu_log.pb.cc",
@@ -3657,7 +3908,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/gpu/gpu_counter_event.pb.h",
     "external/perfetto/protos/perfetto/trace/gpu/gpu_log.pb.h",
@@ -3768,7 +4019,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/interned_data/interned_data.pb.cc",
   ],
@@ -3783,7 +4034,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/interned_data/interned_data.pb.h",
   ],
@@ -3884,7 +4135,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pb.cc",
     "external/perfetto/protos/perfetto/trace/system_info.pb.cc",
@@ -3903,7 +4154,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/clock_snapshot.pb.h",
     "external/perfetto/protos/perfetto/trace/system_info.pb.h",
@@ -4019,7 +4270,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/test_event.pb.cc",
     "external/perfetto/protos/perfetto/trace/trace.pb.cc",
@@ -4040,7 +4291,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/test_event.pb.h",
     "external/perfetto/protos/perfetto/trace/trace.pb.h",
@@ -4146,7 +4397,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.pb.cc",
   ],
@@ -4161,7 +4412,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.pb.h",
   ],
@@ -4257,7 +4508,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/power/battery_counters.pb.cc",
     "external/perfetto/protos/perfetto/trace/power/power_rails.pb.cc",
@@ -4274,7 +4525,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/power/battery_counters.pb.h",
     "external/perfetto/protos/perfetto/trace/power/power_rails.pb.h",
@@ -4416,7 +4667,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.pb.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pb.cc",
@@ -4435,7 +4686,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.pb.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pb.h",
@@ -4541,7 +4792,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ps/process_stats.pb.cc",
     "external/perfetto/protos/perfetto/trace/ps/process_tree.pb.cc",
@@ -4558,7 +4809,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/ps/process_stats.pb.h",
     "external/perfetto/protos/perfetto/trace/ps/process_tree.pb.h",
@@ -4654,7 +4905,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/sys_stats/sys_stats.pb.cc",
   ],
@@ -4669,7 +4920,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/sys_stats/sys_stats.pb.h",
   ],
@@ -4830,7 +5081,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.pb.cc",
@@ -4873,7 +5124,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.pb.h",
@@ -5122,19 +5373,33 @@
   ],
 }
 
-// GN: //src/ipc:ipc
+// GN: //src/ipc:client
 filegroup {
-  name: "perfetto_src_ipc_ipc",
+  name: "perfetto_src_ipc_client",
+  srcs: [
+    "src/ipc/client_impl.cc",
+    "src/ipc/service_proxy.cc",
+  ],
+}
+
+// GN: //src/ipc:common
+filegroup {
+  name: "perfetto_src_ipc_common",
   srcs: [
     "src/ipc/buffered_frame_deserializer.cc",
-    "src/ipc/client_impl.cc",
     "src/ipc/deferred.cc",
-    "src/ipc/host_impl.cc",
-    "src/ipc/service_proxy.cc",
     "src/ipc/virtual_destructors.cc",
   ],
 }
 
+// GN: //src/ipc:host
+filegroup {
+  name: "perfetto_src_ipc_host",
+  srcs: [
+    "src/ipc/host_impl.cc",
+  ],
+}
+
 // GN: //src/ipc:test_messages_cpp
 genrule {
   name: "perfetto_src_ipc_test_messages_cpp_gen",
@@ -5576,7 +5841,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/src/protozero/test/example_proto/library.pb.cc",
     "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pb.cc",
@@ -5597,7 +5862,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/src/protozero/test/example_proto/library.pb.h",
     "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pb.h",
@@ -5726,21 +5991,18 @@
 filegroup {
   name: "perfetto_src_trace_processor_lib",
   srcs: [
-    "src/trace_processor/args_table.cc",
     "src/trace_processor/filtered_row_index.cc",
-    "src/trace_processor/gfp_flags.cc",
-    "src/trace_processor/process_table.cc",
-    "src/trace_processor/raw_table.cc",
     "src/trace_processor/read_trace.cc",
     "src/trace_processor/row_iterators.cc",
     "src/trace_processor/sched_slice_table.cc",
     "src/trace_processor/span_join_operator_table.cc",
     "src/trace_processor/sql_stats_table.cc",
+    "src/trace_processor/sqlite_experimental_flamegraph_table.cc",
+    "src/trace_processor/sqlite_raw_table.cc",
     "src/trace_processor/stats_table.cc",
     "src/trace_processor/storage_columns.cc",
     "src/trace_processor/storage_schema.cc",
     "src/trace_processor/storage_table.cc",
-    "src/trace_processor/thread_table.cc",
     "src/trace_processor/trace_processor.cc",
     "src/trace_processor/trace_processor_impl.cc",
     "src/trace_processor/window_operator_table.cc",
@@ -5830,6 +6092,7 @@
     "src/trace_processor/event_tracker.cc",
     "src/trace_processor/forwarding_trace_parser.cc",
     "src/trace_processor/ftrace_utils.cc",
+    "src/trace_processor/global_args_tracker.cc",
     "src/trace_processor/gzip_trace_parser.cc",
     "src/trace_processor/heap_profile_tracker.cc",
     "src/trace_processor/importers/ftrace/ftrace_module.cc",
@@ -5872,11 +6135,19 @@
   ],
 }
 
+// GN: //src/trace_processor/types:types
+filegroup {
+  name: "perfetto_src_trace_processor_types_types",
+  srcs: [
+    "src/trace_processor/types/gfp_flags.cc",
+    "src/trace_processor/types/variadic.cc",
+  ],
+}
+
 // GN: //src/trace_processor:unittests
 filegroup {
   name: "perfetto_src_trace_processor_unittests",
   srcs: [
-    "src/trace_processor/args_table_unittest.cc",
     "src/trace_processor/clock_tracker_unittest.cc",
     "src/trace_processor/event_tracker_unittest.cc",
     "src/trace_processor/filtered_row_index_unittest.cc",
@@ -5885,17 +6156,16 @@
     "src/trace_processor/heap_profile_tracker_unittest.cc",
     "src/trace_processor/importers/fuchsia/fuchsia_trace_utils_unittest.cc",
     "src/trace_processor/importers/proto/args_table_utils_unittest.cc",
+    "src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
     "src/trace_processor/importers/proto/heap_graph_walker_unittest.cc",
     "src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
     "src/trace_processor/importers/systrace/systrace_parser_unittest.cc",
-    "src/trace_processor/process_table_unittest.cc",
     "src/trace_processor/process_tracker_unittest.cc",
     "src/trace_processor/protozero_to_text_unittests.cc",
     "src/trace_processor/sched_slice_table_unittest.cc",
     "src/trace_processor/slice_tracker_unittest.cc",
     "src/trace_processor/span_join_operator_table_unittest.cc",
     "src/trace_processor/syscall_tracker_unittest.cc",
-    "src/trace_processor/thread_table_unittest.cc",
     "src/trace_processor/trace_sorter_unittest.cc",
   ],
 }
@@ -5987,6 +6257,22 @@
   ],
 }
 
+// GN: //src/traced/probes/ftrace/kallsyms:kallsyms
+filegroup {
+  name: "perfetto_src_traced_probes_ftrace_kallsyms_kallsyms",
+  srcs: [
+    "src/traced/probes/ftrace/kallsyms/kernel_symbol_map.cc",
+  ],
+}
+
+// GN: //src/traced/probes/ftrace/kallsyms:unittests
+filegroup {
+  name: "perfetto_src_traced_probes_ftrace_kallsyms_unittests",
+  srcs: [
+    "src/traced/probes/ftrace/kallsyms/kernel_symbol_map_unittest.cc",
+  ],
+}
+
 // GN: //src/traced/probes/ftrace:test_messages_cpp
 genrule {
   name: "perfetto_src_traced_probes_ftrace_test_messages_cpp_gen",
@@ -6032,7 +6318,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/src/traced/probes/ftrace/test/test_messages.pb.cc",
   ],
@@ -6047,7 +6333,7 @@
   tools: [
     "aprotoc",
   ],
-  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=$(genDir)/external/perfetto/ $(in)",
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/src/traced/probes/ftrace/test/test_messages.pb.h",
   ],
@@ -6219,32 +6505,26 @@
   ],
 }
 
-// GN: //src/tracing:client_api
+// GN: //src/tracing:client_api_base
 filegroup {
-  name: "perfetto_src_tracing_client_api",
+  name: "perfetto_src_tracing_client_api_base",
   srcs: [
     "src/tracing/data_source.cc",
     "src/tracing/debug_annotation.cc",
     "src/tracing/event_context.cc",
-    "src/tracing/internal/in_process_tracing_backend.cc",
-    "src/tracing/internal/system_tracing_backend.cc",
     "src/tracing/internal/tracing_muxer_impl.cc",
     "src/tracing/internal/track_event_internal.cc",
     "src/tracing/platform.cc",
     "src/tracing/tracing.cc",
+    "src/tracing/track.cc",
     "src/tracing/track_event_category_registry.cc",
     "src/tracing/virtual_destructors.cc",
   ],
 }
 
-// GN: //src/tracing:client_api_integrationtests
+// GN: //src/tracing:client_api_system_backend_only
 filegroup {
-  name: "perfetto_src_tracing_client_api_integrationtests",
-  srcs: [
-    "src/tracing/api_integrationtest.cc",
-    "src/tracing/test/tracing_module.cc",
-    "src/tracing/test/tracing_module2.cc",
-  ],
+  name: "perfetto_src_tracing_client_api_system_backend_only",
 }
 
 // GN: //src/tracing:common
@@ -6255,75 +6535,52 @@
   ],
 }
 
-// GN: //src/tracing:consumer_api_deprecated
+// GN: //src/tracing/consumer_api_deprecated:consumer_api_deprecated
 filegroup {
-  name: "perfetto_src_tracing_consumer_api_deprecated",
+  name: "perfetto_src_tracing_consumer_api_deprecated_consumer_api_deprecated",
   srcs: [
-    "src/tracing/api_impl/consumer_api.cc",
+    "src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc",
   ],
 }
 
-// GN: //src/tracing:ipc
+// GN: //src/tracing/core:core
 filegroup {
-  name: "perfetto_src_tracing_ipc",
+  name: "perfetto_src_tracing_core_core",
   srcs: [
-    "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
-    "src/tracing/ipc/default_socket.cc",
-    "src/tracing/ipc/posix_shared_memory.cc",
-    "src/tracing/ipc/producer/producer_ipc_client_impl.cc",
-    "src/tracing/ipc/service/consumer_ipc_service.cc",
-    "src/tracing/ipc/service/producer_ipc_service.cc",
-    "src/tracing/ipc/service/service_ipc_host_impl.cc",
+    "src/tracing/core/id_allocator.cc",
+    "src/tracing/core/null_trace_writer.cc",
+    "src/tracing/core/shared_memory_abi.cc",
+    "src/tracing/core/shared_memory_arbiter_impl.cc",
+    "src/tracing/core/startup_trace_writer.cc",
+    "src/tracing/core/startup_trace_writer_registry.cc",
+    "src/tracing/core/trace_packet.cc",
+    "src/tracing/core/trace_writer_impl.cc",
+    "src/tracing/core/virtual_destructors.cc",
   ],
 }
 
-// GN: //src/tracing:platform_posix
+// GN: //src/tracing/core:service
 filegroup {
-  name: "perfetto_src_tracing_platform_posix",
+  name: "perfetto_src_tracing_core_service",
   srcs: [
-    "src/tracing/platform_posix.cc",
+    "src/tracing/core/metatrace_writer.cc",
+    "src/tracing/core/packet_stream_validator.cc",
+    "src/tracing/core/trace_buffer.cc",
+    "src/tracing/core/tracing_service_impl.cc",
   ],
 }
 
-// GN: //src/tracing/test:api_test_support
+// GN: //src/tracing/core:test_support
 filegroup {
-  name: "perfetto_src_tracing_test_api_test_support",
-  srcs: [
-    "src/tracing/test/api_test_support.cc",
-  ],
-}
-
-// GN: //src/tracing:test_support
-filegroup {
-  name: "perfetto_src_tracing_test_support",
+  name: "perfetto_src_tracing_core_test_support",
   srcs: [
     "src/tracing/core/trace_writer_for_testing.cc",
   ],
 }
 
-// GN: //src/tracing:tracing
+// GN: //src/tracing/core:unittests
 filegroup {
-  name: "perfetto_src_tracing_tracing",
-  srcs: [
-    "src/tracing/core/id_allocator.cc",
-    "src/tracing/core/metatrace_writer.cc",
-    "src/tracing/core/null_trace_writer.cc",
-    "src/tracing/core/packet_stream_validator.cc",
-    "src/tracing/core/shared_memory_abi.cc",
-    "src/tracing/core/shared_memory_arbiter_impl.cc",
-    "src/tracing/core/startup_trace_writer.cc",
-    "src/tracing/core/startup_trace_writer_registry.cc",
-    "src/tracing/core/trace_buffer.cc",
-    "src/tracing/core/trace_packet.cc",
-    "src/tracing/core/trace_writer_impl.cc",
-    "src/tracing/core/tracing_service_impl.cc",
-    "src/tracing/core/virtual_destructors.cc",
-  ],
-}
-
-// GN: //src/tracing:unittests
-filegroup {
-  name: "perfetto_src_tracing_unittests",
+  name: "perfetto_src_tracing_core_unittests",
   srcs: [
     "src/tracing/core/id_allocator_unittest.cc",
     "src/tracing/core/null_trace_writer_unittest.cc",
@@ -6336,12 +6593,118 @@
     "src/tracing/core/trace_packet_unittest.cc",
     "src/tracing/core/trace_writer_impl_unittest.cc",
     "src/tracing/core/tracing_service_impl_unittest.cc",
+  ],
+}
+
+// GN: //src/tracing:in_process_backend
+filegroup {
+  name: "perfetto_src_tracing_in_process_backend",
+  srcs: [
+    "src/tracing/internal/in_process_tracing_backend.cc",
+  ],
+}
+
+// GN: //src/tracing:in_process_backend_fake
+filegroup {
+  name: "perfetto_src_tracing_in_process_backend_fake",
+  srcs: [
+    "src/tracing/internal/in_process_tracing_backend_fake.cc",
+  ],
+}
+
+// GN: //src/tracing/ipc:common
+filegroup {
+  name: "perfetto_src_tracing_ipc_common",
+  srcs: [
+    "src/tracing/ipc/default_socket.cc",
+    "src/tracing/ipc/posix_shared_memory.cc",
+  ],
+}
+
+// GN: //src/tracing/ipc/consumer:consumer
+filegroup {
+  name: "perfetto_src_tracing_ipc_consumer_consumer",
+  srcs: [
+    "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
+  ],
+}
+
+// GN: //src/tracing/ipc/producer:producer
+filegroup {
+  name: "perfetto_src_tracing_ipc_producer_producer",
+  srcs: [
+    "src/tracing/ipc/producer/producer_ipc_client_impl.cc",
+  ],
+}
+
+// GN: //src/tracing/ipc/service:service
+filegroup {
+  name: "perfetto_src_tracing_ipc_service_service",
+  srcs: [
+    "src/tracing/ipc/service/consumer_ipc_service.cc",
+    "src/tracing/ipc/service/producer_ipc_service.cc",
+    "src/tracing/ipc/service/service_ipc_host_impl.cc",
+  ],
+}
+
+// GN: //src/tracing/ipc:unittests
+filegroup {
+  name: "perfetto_src_tracing_ipc_unittests",
+  srcs: [
     "src/tracing/ipc/posix_shared_memory_unittest.cc",
+  ],
+}
+
+// GN: //src/tracing:platform_posix
+filegroup {
+  name: "perfetto_src_tracing_platform_posix",
+  srcs: [
+    "src/tracing/platform_posix.cc",
+  ],
+}
+
+// GN: //src/tracing:system_process_backend
+filegroup {
+  name: "perfetto_src_tracing_system_process_backend",
+  srcs: [
+    "src/tracing/internal/system_tracing_backend.cc",
+  ],
+}
+
+// GN: //src/tracing/test:api_test_support
+filegroup {
+  name: "perfetto_src_tracing_test_api_test_support",
+  srcs: [
+    "src/tracing/test/api_test_support.cc",
+  ],
+}
+
+// GN: //src/tracing/test:client_api_integrationtests
+filegroup {
+  name: "perfetto_src_tracing_test_client_api_integrationtests",
+  srcs: [
+    "src/tracing/test/api_integrationtest.cc",
+    "src/tracing/test/tracing_module.cc",
+    "src/tracing/test/tracing_module2.cc",
+  ],
+}
+
+// GN: //src/tracing/test:test_support
+filegroup {
+  name: "perfetto_src_tracing_test_test_support",
+  srcs: [
     "src/tracing/test/aligned_buffer_test.cc",
     "src/tracing/test/fake_packet.cc",
     "src/tracing/test/mock_consumer.cc",
     "src/tracing/test/mock_producer.cc",
     "src/tracing/test/test_shared_memory.cc",
+  ],
+}
+
+// GN: //src/tracing/test:tracing_integration_test
+filegroup {
+  name: "perfetto_src_tracing_test_tracing_integration_test",
+  srcs: [
     "src/tracing/test/tracing_integration_test.cc",
   ],
 }
@@ -6354,27 +6717,11 @@
   ],
 }
 
-// GN: //test:task_runner_thread
-filegroup {
-  name: "perfetto_test_task_runner_thread",
-  srcs: [
-    "test/task_runner_thread.cc",
-  ],
-}
-
-// GN: //test:task_runner_thread_delegates
-filegroup {
-  name: "perfetto_test_task_runner_thread_delegates",
-  srcs: [
-    "test/fake_producer.cc",
-    "test/task_runner_thread_delegates.cc",
-  ],
-}
-
 // GN: //test:test_helper
 filegroup {
   name: "perfetto_test_test_helper",
   srcs: [
+    "test/fake_producer.cc",
     "test/test_helper.cc",
   ],
 }
@@ -6603,7 +6950,9 @@
     ":perfetto_src_base_test_support",
     ":perfetto_src_base_unittests",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
+    ":perfetto_src_ipc_host",
     ":perfetto_src_ipc_test_messages_cpp_gen",
     ":perfetto_src_ipc_test_messages_ipc_gen",
     ":perfetto_src_ipc_unittests",
@@ -6645,6 +6994,7 @@
     ":perfetto_src_trace_processor_storage_minimal",
     ":perfetto_src_trace_processor_tables_tables",
     ":perfetto_src_trace_processor_tables_unittests",
+    ":perfetto_src_trace_processor_types_types",
     ":perfetto_src_trace_processor_unittests",
     ":perfetto_src_traced_probes_android_log_android_log",
     ":perfetto_src_traced_probes_android_log_unittests",
@@ -6653,6 +7003,8 @@
     ":perfetto_src_traced_probes_filesystem_unittests",
     ":perfetto_src_traced_probes_ftrace_format_parser",
     ":perfetto_src_traced_probes_ftrace_ftrace",
+    ":perfetto_src_traced_probes_ftrace_kallsyms_kallsyms",
+    ":perfetto_src_traced_probes_ftrace_kallsyms_unittests",
     ":perfetto_src_traced_probes_ftrace_test_messages_cpp_gen",
     ":perfetto_src_traced_probes_ftrace_test_messages_lite_gen",
     ":perfetto_src_traced_probes_ftrace_test_messages_zero_gen",
@@ -6671,10 +7023,17 @@
     ":perfetto_src_traced_service_service",
     ":perfetto_src_traced_service_unittests",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_test_support",
-    ":perfetto_src_tracing_tracing",
-    ":perfetto_src_tracing_unittests",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_core_test_support",
+    ":perfetto_src_tracing_core_unittests",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_consumer_consumer",
+    ":perfetto_src_tracing_ipc_producer_producer",
+    ":perfetto_src_tracing_ipc_service_service",
+    ":perfetto_src_tracing_ipc_unittests",
+    ":perfetto_src_tracing_test_test_support",
+    ":perfetto_src_tracing_test_tracing_integration_test",
     ":perfetto_tools_sanitizers_unittests_sanitizers_unittests",
   ],
   shared_libs: [
@@ -6859,6 +7218,7 @@
     ":perfetto_src_trace_processor_storage_full",
     ":perfetto_src_trace_processor_storage_minimal",
     ":perfetto_src_trace_processor_tables_tables",
+    ":perfetto_src_trace_processor_types_types",
     "src/trace_processor/proto_to_json.cc",
     "src/trace_processor/trace_processor_shell.cc",
   ],
@@ -6968,6 +7328,7 @@
     ":perfetto_src_trace_processor_storage_full",
     ":perfetto_src_trace_processor_storage_minimal",
     ":perfetto_src_trace_processor_tables_tables",
+    ":perfetto_src_trace_processor_types_types",
     ":perfetto_tools_trace_to_text_common",
     ":perfetto_tools_trace_to_text_full",
     ":perfetto_tools_trace_to_text_pprofbuilder",
@@ -7092,14 +7453,16 @@
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
     ":perfetto_src_profiling_perf_producer",
     ":perfetto_src_profiling_perf_traced_perf_main",
     ":perfetto_src_profiling_perf_unwind_support",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_producer_producer",
     "src/profiling/perf/main.cc",
   ],
   shared_libs: [
@@ -7232,14 +7595,16 @@
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
-    ":perfetto_src_ipc_ipc",
+    ":perfetto_src_ipc_client",
+    ":perfetto_src_ipc_common",
     ":perfetto_src_perfetto_cmd_protos_gen",
     ":perfetto_src_perfetto_cmd_trigger_perfetto_cmd",
     ":perfetto_src_perfetto_cmd_trigger_producer",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_producer_producer",
     "src/perfetto_cmd/trigger_perfetto_main.cc",
   ],
   shared_libs: [
@@ -7296,25 +7661,6 @@
 
 // These targets are appended to the autogenerated Android.bp by tools/gen_android_bp.
 
-cc_library_static {
-  name: "perfetto_cts_jni_deps",
-  srcs: [
-    "src/base/test/test_task_runner.cc",
-    "test/fake_producer.cc",
-    "test/task_runner_thread_delegates.cc",
-  ],
-  shared_libs: [
-    "libprotobuf-cpp-lite",
-  ],
-  static_libs: [
-    "libgtest",
-    "libperfetto_client_experimental",
-  ],
-  defaults: [
-    "perfetto_defaults",
-  ],
-}
-
 java_library_host {
   name: "perfetto_config-full",
   proto: {
diff --git a/Android.bp.extras b/Android.bp.extras
index 9586516..65a81f4 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -1,24 +1,5 @@
 // These targets are appended to the autogenerated Android.bp by tools/gen_android_bp.
 
-cc_library_static {
-  name: "perfetto_cts_jni_deps",
-  srcs: [
-    "src/base/test/test_task_runner.cc",
-    "test/fake_producer.cc",
-    "test/task_runner_thread_delegates.cc",
-  ],
-  shared_libs: [
-    "libprotobuf-cpp-lite",
-  ],
-  static_libs: [
-    "libgtest",
-    "libperfetto_client_experimental",
-  ],
-  defaults: [
-    "perfetto_defaults",
-  ],
-}
-
 java_library_host {
   name: "perfetto_config-full",
   proto: {
diff --git a/BUILD b/BUILD
index 5303bde..1d891cc 100644
--- a/BUILD
+++ b/BUILD
@@ -58,7 +58,9 @@
     srcs = [
         ":src_base_base",
         ":src_base_unix_socket",
-        ":src_ipc_ipc",
+        ":src_ipc_client",
+        ":src_ipc_common",
+        ":src_ipc_host",
     ],
     hdrs = [
         ":include_perfetto_base_base",
@@ -161,7 +163,9 @@
         ":src_android_internal_lazy_library_loader",
         ":src_base_base",
         ":src_base_unix_socket",
-        ":src_ipc_ipc",
+        ":src_ipc_client",
+        ":src_ipc_common",
+        ":src_ipc_host",
         ":src_perfetto_cmd_perfetto_atoms",
         ":src_protozero_protozero",
         ":src_traced_probes_android_log_android_log",
@@ -178,9 +182,13 @@
         ":src_traced_probes_sys_stats_sys_stats",
         ":src_traced_service_service",
         ":src_tracing_common",
-        ":src_tracing_consumer_api_deprecated",
-        ":src_tracing_ipc",
-        ":src_tracing_tracing",
+        ":src_tracing_consumer_api_deprecated_consumer_api_deprecated",
+        ":src_tracing_core_core",
+        ":src_tracing_core_service",
+        ":src_tracing_ipc_common",
+        ":src_tracing_ipc_consumer_consumer",
+        ":src_tracing_ipc_producer_producer",
+        ":src_tracing_ipc_service_service",
     ],
     hdrs = [
         ":include_perfetto_base_base",
@@ -245,8 +253,11 @@
         "include/perfetto/base/build_config.h",
         "include/perfetto/base/compiler.h",
         "include/perfetto/base/export.h",
+        "include/perfetto/base/flat_set.h",
         "include/perfetto/base/logging.h",
+        "include/perfetto/base/proc_utils.h",
         "include/perfetto/base/task_runner.h",
+        "include/perfetto/base/thread_utils.h",
         "include/perfetto/base/time.h",
     ],
 )
@@ -259,7 +270,6 @@
         "include/perfetto/ext/base/container_annotations.h",
         "include/perfetto/ext/base/event_fd.h",
         "include/perfetto/ext/base/file_utils.h",
-        "include/perfetto/ext/base/flat_set.h",
         "include/perfetto/ext/base/hash.h",
         "include/perfetto/ext/base/lookup_set.h",
         "include/perfetto/ext/base/metatrace.h",
@@ -268,7 +278,6 @@
         "include/perfetto/ext/base/optional.h",
         "include/perfetto/ext/base/paged_memory.h",
         "include/perfetto/ext/base/pipe.h",
-        "include/perfetto/ext/base/proc_utils.h",
         "include/perfetto/ext/base/scoped_file.h",
         "include/perfetto/ext/base/small_set.h",
         "include/perfetto/ext/base/string_splitter.h",
@@ -279,7 +288,6 @@
         "include/perfetto/ext/base/thread_annotations.h",
         "include/perfetto/ext/base/thread_checker.h",
         "include/perfetto/ext/base/thread_task_runner.h",
-        "include/perfetto/ext/base/thread_utils.h",
         "include/perfetto/ext/base/unix_socket.h",
         "include/perfetto/ext/base/unix_task_runner.h",
         "include/perfetto/ext/base/utils.h",
@@ -477,9 +485,11 @@
         "include/perfetto/tracing/trace_writer_base.h",
         "include/perfetto/tracing/tracing.h",
         "include/perfetto/tracing/tracing_backend.h",
+        "include/perfetto/tracing/track.h",
         "include/perfetto/tracing/track_event.h",
         "include/perfetto/tracing/track_event_category_registry.h",
         "include/perfetto/tracing/track_event_interned_data_index.h",
+        "include/perfetto/tracing/track_event_legacy.h",
     ],
 )
 
@@ -538,19 +548,33 @@
     ],
 )
 
-# GN target: //src/ipc:ipc
+# GN target: //src/ipc:client
 filegroup(
-    name = "src_ipc_ipc",
+    name = "src_ipc_client",
+    srcs = [
+        "src/ipc/client_impl.cc",
+        "src/ipc/client_impl.h",
+        "src/ipc/service_proxy.cc",
+    ],
+)
+
+# GN target: //src/ipc:common
+filegroup(
+    name = "src_ipc_common",
     srcs = [
         "src/ipc/buffered_frame_deserializer.cc",
         "src/ipc/buffered_frame_deserializer.h",
-        "src/ipc/client_impl.cc",
-        "src/ipc/client_impl.h",
         "src/ipc/deferred.cc",
+        "src/ipc/virtual_destructors.cc",
+    ],
+)
+
+# GN target: //src/ipc:host
+filegroup(
+    name = "src_ipc_host",
+    srcs = [
         "src/ipc/host_impl.cc",
         "src/ipc/host_impl.h",
-        "src/ipc/service_proxy.cc",
-        "src/ipc/virtual_destructors.cc",
     ],
 )
 
@@ -661,6 +685,7 @@
         "src/trace_processor/db/table.cc",
         "src/trace_processor/db/table.h",
         "src/trace_processor/db/typed_column.h",
+        "src/trace_processor/db/typed_column_internal.h",
     ],
 )
 
@@ -670,6 +695,7 @@
         "src/trace_processor/metrics/android/android_batt.sql",
         "src/trace_processor/metrics/android/android_cpu.sql",
         "src/trace_processor/metrics/android/android_cpu_agg.sql",
+        "src/trace_processor/metrics/android/android_hwui_metric.sql",
         "src/trace_processor/metrics/android/android_ion.sql",
         "src/trace_processor/metrics/android/android_lmk.sql",
         "src/trace_processor/metrics/android/android_lmk_reason.sql",
@@ -707,6 +733,7 @@
 filegroup(
     name = "src_trace_processor_metrics_lib",
     srcs = [
+        "src/trace_processor/metrics/custom_options.descriptor.h",
         "src/trace_processor/metrics/metrics.cc",
         "src/trace_processor/metrics/metrics.descriptor.h",
         "src/trace_processor/metrics/metrics.h",
@@ -763,6 +790,17 @@
     ],
 )
 
+# GN target: //src/trace_processor/types:types
+filegroup(
+    name = "src_trace_processor_types_types",
+    srcs = [
+        "src/trace_processor/types/gfp_flags.cc",
+        "src/trace_processor/types/gfp_flags.h",
+        "src/trace_processor/types/variadic.cc",
+        "src/trace_processor/types/variadic.h",
+    ],
+)
+
 # GN target: //src/trace_processor:descriptors
 filegroup(
     name = "src_trace_processor_descriptors",
@@ -785,16 +823,8 @@
 filegroup(
     name = "src_trace_processor_lib",
     srcs = [
-        "src/trace_processor/args_table.cc",
-        "src/trace_processor/args_table.h",
         "src/trace_processor/filtered_row_index.cc",
         "src/trace_processor/filtered_row_index.h",
-        "src/trace_processor/gfp_flags.cc",
-        "src/trace_processor/gfp_flags.h",
-        "src/trace_processor/process_table.cc",
-        "src/trace_processor/process_table.h",
-        "src/trace_processor/raw_table.cc",
-        "src/trace_processor/raw_table.h",
         "src/trace_processor/read_trace.cc",
         "src/trace_processor/row_iterators.cc",
         "src/trace_processor/row_iterators.h",
@@ -804,6 +834,10 @@
         "src/trace_processor/span_join_operator_table.h",
         "src/trace_processor/sql_stats_table.cc",
         "src/trace_processor/sql_stats_table.h",
+        "src/trace_processor/sqlite_experimental_flamegraph_table.cc",
+        "src/trace_processor/sqlite_experimental_flamegraph_table.h",
+        "src/trace_processor/sqlite_raw_table.cc",
+        "src/trace_processor/sqlite_raw_table.h",
         "src/trace_processor/stats_table.cc",
         "src/trace_processor/stats_table.h",
         "src/trace_processor/storage_columns.cc",
@@ -812,8 +846,6 @@
         "src/trace_processor/storage_schema.h",
         "src/trace_processor/storage_table.cc",
         "src/trace_processor/storage_table.h",
-        "src/trace_processor/thread_table.cc",
-        "src/trace_processor/thread_table.h",
         "src/trace_processor/trace_processor.cc",
         "src/trace_processor/trace_processor_impl.cc",
         "src/trace_processor/trace_processor_impl.h",
@@ -891,6 +923,8 @@
         "src/trace_processor/forwarding_trace_parser.h",
         "src/trace_processor/ftrace_utils.cc",
         "src/trace_processor/ftrace_utils.h",
+        "src/trace_processor/global_args_tracker.cc",
+        "src/trace_processor/global_args_tracker.h",
         "src/trace_processor/gzip_trace_parser.cc",
         "src/trace_processor/gzip_trace_parser.h",
         "src/trace_processor/heap_profile_tracker.cc",
@@ -954,7 +988,6 @@
         "src/trace_processor/trace_storage.h",
         "src/trace_processor/track_tracker.cc",
         "src/trace_processor/track_tracker.h",
-        "src/trace_processor/variadic.h",
         "src/trace_processor/virtual_destructors.cc",
     ],
 )
@@ -1113,22 +1146,106 @@
     ],
 )
 
-# GN target: //src/tracing:client_api
+# GN target: //src/tracing/consumer_api_deprecated:consumer_api_deprecated
 filegroup(
-    name = "src_tracing_client_api",
+    name = "src_tracing_consumer_api_deprecated_consumer_api_deprecated",
+    srcs = [
+        "src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc",
+    ],
+)
+
+# GN target: //src/tracing/core:core
+filegroup(
+    name = "src_tracing_core_core",
+    srcs = [
+        "src/tracing/core/id_allocator.cc",
+        "src/tracing/core/id_allocator.h",
+        "src/tracing/core/null_trace_writer.cc",
+        "src/tracing/core/null_trace_writer.h",
+        "src/tracing/core/patch_list.h",
+        "src/tracing/core/shared_memory_abi.cc",
+        "src/tracing/core/shared_memory_arbiter_impl.cc",
+        "src/tracing/core/shared_memory_arbiter_impl.h",
+        "src/tracing/core/startup_trace_writer.cc",
+        "src/tracing/core/startup_trace_writer_registry.cc",
+        "src/tracing/core/trace_packet.cc",
+        "src/tracing/core/trace_writer_impl.cc",
+        "src/tracing/core/trace_writer_impl.h",
+        "src/tracing/core/virtual_destructors.cc",
+    ],
+)
+
+# GN target: //src/tracing/core:service
+filegroup(
+    name = "src_tracing_core_service",
+    srcs = [
+        "src/tracing/core/metatrace_writer.cc",
+        "src/tracing/core/metatrace_writer.h",
+        "src/tracing/core/packet_stream_validator.cc",
+        "src/tracing/core/packet_stream_validator.h",
+        "src/tracing/core/trace_buffer.cc",
+        "src/tracing/core/trace_buffer.h",
+        "src/tracing/core/tracing_service_impl.cc",
+        "src/tracing/core/tracing_service_impl.h",
+    ],
+)
+
+# GN target: //src/tracing/ipc/consumer:consumer
+filegroup(
+    name = "src_tracing_ipc_consumer_consumer",
+    srcs = [
+        "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
+        "src/tracing/ipc/consumer/consumer_ipc_client_impl.h",
+    ],
+)
+
+# GN target: //src/tracing/ipc/producer:producer
+filegroup(
+    name = "src_tracing_ipc_producer_producer",
+    srcs = [
+        "src/tracing/ipc/producer/producer_ipc_client_impl.cc",
+        "src/tracing/ipc/producer/producer_ipc_client_impl.h",
+    ],
+)
+
+# GN target: //src/tracing/ipc/service:service
+filegroup(
+    name = "src_tracing_ipc_service_service",
+    srcs = [
+        "src/tracing/ipc/service/consumer_ipc_service.cc",
+        "src/tracing/ipc/service/consumer_ipc_service.h",
+        "src/tracing/ipc/service/producer_ipc_service.cc",
+        "src/tracing/ipc/service/producer_ipc_service.h",
+        "src/tracing/ipc/service/service_ipc_host_impl.cc",
+        "src/tracing/ipc/service/service_ipc_host_impl.h",
+    ],
+)
+
+# GN target: //src/tracing/ipc:common
+filegroup(
+    name = "src_tracing_ipc_common",
+    srcs = [
+        "src/tracing/ipc/default_socket.cc",
+        "src/tracing/ipc/posix_shared_memory.cc",
+        "src/tracing/ipc/posix_shared_memory.h",
+    ],
+)
+
+# GN target: //src/tracing:client_api_base
+filegroup(
+    name = "src_tracing_client_api_base",
     srcs = [
         "src/tracing/data_source.cc",
         "src/tracing/debug_annotation.cc",
         "src/tracing/event_context.cc",
-        "src/tracing/internal/in_process_tracing_backend.cc",
         "src/tracing/internal/in_process_tracing_backend.h",
-        "src/tracing/internal/system_tracing_backend.cc",
         "src/tracing/internal/system_tracing_backend.h",
         "src/tracing/internal/tracing_muxer_impl.cc",
         "src/tracing/internal/tracing_muxer_impl.h",
         "src/tracing/internal/track_event_internal.cc",
         "src/tracing/platform.cc",
         "src/tracing/tracing.cc",
+        "src/tracing/track.cc",
         "src/tracing/track_event_category_registry.cc",
         "src/tracing/virtual_destructors.cc",
     ],
@@ -1142,31 +1259,11 @@
     ],
 )
 
-# GN target: //src/tracing:consumer_api_deprecated
+# GN target: //src/tracing:in_process_backend
 filegroup(
-    name = "src_tracing_consumer_api_deprecated",
+    name = "src_tracing_in_process_backend",
     srcs = [
-        "src/tracing/api_impl/consumer_api.cc",
-    ],
-)
-
-# GN target: //src/tracing:ipc
-filegroup(
-    name = "src_tracing_ipc",
-    srcs = [
-        "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
-        "src/tracing/ipc/consumer/consumer_ipc_client_impl.h",
-        "src/tracing/ipc/default_socket.cc",
-        "src/tracing/ipc/posix_shared_memory.cc",
-        "src/tracing/ipc/posix_shared_memory.h",
-        "src/tracing/ipc/producer/producer_ipc_client_impl.cc",
-        "src/tracing/ipc/producer/producer_ipc_client_impl.h",
-        "src/tracing/ipc/service/consumer_ipc_service.cc",
-        "src/tracing/ipc/service/consumer_ipc_service.h",
-        "src/tracing/ipc/service/producer_ipc_service.cc",
-        "src/tracing/ipc/service/producer_ipc_service.h",
-        "src/tracing/ipc/service/service_ipc_host_impl.cc",
-        "src/tracing/ipc/service/service_ipc_host_impl.h",
+        "src/tracing/internal/in_process_tracing_backend.cc",
     ],
 )
 
@@ -1178,32 +1275,11 @@
     ],
 )
 
-# GN target: //src/tracing:tracing
+# GN target: //src/tracing:system_process_backend
 filegroup(
-    name = "src_tracing_tracing",
+    name = "src_tracing_system_process_backend",
     srcs = [
-        "src/tracing/core/id_allocator.cc",
-        "src/tracing/core/id_allocator.h",
-        "src/tracing/core/metatrace_writer.cc",
-        "src/tracing/core/metatrace_writer.h",
-        "src/tracing/core/null_trace_writer.cc",
-        "src/tracing/core/null_trace_writer.h",
-        "src/tracing/core/packet_stream_validator.cc",
-        "src/tracing/core/packet_stream_validator.h",
-        "src/tracing/core/patch_list.h",
-        "src/tracing/core/shared_memory_abi.cc",
-        "src/tracing/core/shared_memory_arbiter_impl.cc",
-        "src/tracing/core/shared_memory_arbiter_impl.h",
-        "src/tracing/core/startup_trace_writer.cc",
-        "src/tracing/core/startup_trace_writer_registry.cc",
-        "src/tracing/core/trace_buffer.cc",
-        "src/tracing/core/trace_buffer.h",
-        "src/tracing/core/trace_packet.cc",
-        "src/tracing/core/trace_writer_impl.cc",
-        "src/tracing/core/trace_writer_impl.h",
-        "src/tracing/core/tracing_service_impl.cc",
-        "src/tracing/core/tracing_service_impl.h",
-        "src/tracing/core/virtual_destructors.cc",
+        "src/tracing/internal/system_tracing_backend.cc",
     ],
 )
 
@@ -1486,9 +1562,7 @@
     srcs = [
         "protos/perfetto/config/perfetto_config.proto",
     ],
-    visibility = [
-        "//visibility:public",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 # GN target: //protos/perfetto/config/power:cpp
@@ -1607,9 +1681,7 @@
         "protos/perfetto/config/test_config.proto",
         "protos/perfetto/config/trace_config.proto",
     ],
-    visibility = [
-        "//visibility:public",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
     deps = [
         ":protos_perfetto_common_protos",
         ":protos_perfetto_config_android_protos",
@@ -1767,6 +1839,7 @@
         "protos/perfetto/metrics/android/batt_metric.proto",
         "protos/perfetto/metrics/android/cpu_metric.proto",
         "protos/perfetto/metrics/android/heap_profile_callsites.proto",
+        "protos/perfetto/metrics/android/hwui_metric.proto",
         "protos/perfetto/metrics/android/ion_metric.proto",
         "protos/perfetto/metrics/android/java_heap_stats.proto",
         "protos/perfetto/metrics/android/lmk_metric.proto",
@@ -1780,9 +1853,7 @@
         "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
         "protos/perfetto/metrics/android/unsymbolized_frames.proto",
     ],
-    visibility = [
-        "//visibility:public",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 # GN target: //protos/perfetto/metrics/android:zero
@@ -1807,9 +1878,7 @@
     srcs = [
         "protos/perfetto/metrics/metrics.proto",
     ],
-    visibility = [
-        "//visibility:public",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
     deps = [
         ":protos_perfetto_metrics_android_protos",
     ],
@@ -2050,9 +2119,7 @@
     srcs = [
         "protos/perfetto/trace/perfetto_trace.proto",
     ],
-    visibility = [
-        "//visibility:public",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 # GN target: //protos/perfetto/trace:minimal_lite
@@ -2113,9 +2180,7 @@
         "protos/perfetto/trace/trace_packet.proto",
         "protos/perfetto/trace/trace_packet_defaults.proto",
     ],
-    visibility = [
-        "//visibility:public",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
     deps = [
         ":protos_perfetto_common_protos",
         ":protos_perfetto_config_android_protos",
@@ -2420,13 +2485,21 @@
     srcs = [
         ":src_base_base",
         ":src_base_unix_socket",
-        ":src_ipc_ipc",
+        ":src_ipc_client",
+        ":src_ipc_common",
+        ":src_ipc_host",
         ":src_protozero_protozero",
-        ":src_tracing_client_api",
+        ":src_tracing_client_api_base",
         ":src_tracing_common",
-        ":src_tracing_ipc",
+        ":src_tracing_core_core",
+        ":src_tracing_core_service",
+        ":src_tracing_in_process_backend",
+        ":src_tracing_ipc_common",
+        ":src_tracing_ipc_consumer_consumer",
+        ":src_tracing_ipc_producer_producer",
+        ":src_tracing_ipc_service_service",
         ":src_tracing_platform_posix",
-        ":src_tracing_tracing",
+        ":src_tracing_system_process_backend",
     ],
     hdrs = [
         ":include_perfetto_base_base",
@@ -2504,14 +2577,17 @@
         ":src_android_internal_lazy_library_loader",
         ":src_base_base",
         ":src_base_unix_socket",
-        ":src_ipc_ipc",
+        ":src_ipc_client",
+        ":src_ipc_common",
         ":src_perfetto_cmd_perfetto_atoms",
         ":src_perfetto_cmd_perfetto_cmd",
         ":src_perfetto_cmd_trigger_producer",
         ":src_protozero_protozero",
         ":src_tracing_common",
-        ":src_tracing_ipc",
-        ":src_tracing_tracing",
+        ":src_tracing_core_core",
+        ":src_tracing_ipc_common",
+        ":src_tracing_ipc_consumer_consumer",
+        ":src_tracing_ipc_producer_producer",
     ],
     visibility = [
         "//visibility:public",
@@ -2574,6 +2650,7 @@
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_types_types",
     ],
     hdrs = [
         ":include_perfetto_base_base",
@@ -2657,6 +2734,7 @@
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_types_types",
     ],
     visibility = [
         "//visibility:public",
@@ -2812,6 +2890,7 @@
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_types_types",
         ":tools_trace_to_text_common",
         ":tools_trace_to_text_full",
         ":tools_trace_to_text_pprofbuilder",
diff --git a/BUILD.gn b/BUILD.gn
index b89b558..c57fd69 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -39,7 +39,7 @@
   ]
 }
 
-if (enable_perfetto_trace_processor) {
+if (enable_perfetto_trace_processor && enable_perfetto_trace_processor_sqlite) {
   all_targets += [ "src/trace_processor:trace_processor_shell" ]
 }
 
@@ -82,7 +82,7 @@
 }
 
 if (enable_perfetto_trace_processor_json) {
-  executable("trace_processor_minimal_smoke_tests") {
+  test("trace_processor_minimal_smoke_tests") {
     testonly = true
     deps = [
       "gn:default_deps",
@@ -114,7 +114,7 @@
 # compile-time checks for the CI.
 if (perfetto_build_standalone) {
   all_targets += [
-    "src/tracing:consumer_api_test",
+    "src/tracing/consumer_api_deprecated:consumer_api_test",
     "test/configs",
 
     # For syntax-checking the proto.
@@ -132,6 +132,9 @@
 
     # Used in the when updating the ftrace protos
     "protos/perfetto/trace/ftrace:descriptor",
+
+    # Checks that the "fake" backend implementations build.
+    "src/tracing:client_api_no_backends_compile_test",
   ]
 }
 
@@ -155,9 +158,7 @@
 # depending on other GN files.
 group("default") {
   testonly = true
-  deps = [
-    ":all",
-  ]
+  deps = [ ":all" ]
 }
 
 # +----------------------------------------------------------------------------+
@@ -166,9 +167,7 @@
 
 if (enable_perfetto_ui) {
   group("ui") {
-    deps = [
-      "ui",
-    ]
+    deps = [ "ui" ]
   }
 }
 
@@ -198,13 +197,13 @@
       "gn:default_deps",
       "src/traced/probes",
       "src/traced/service",
-      "src/tracing:consumer_api_deprecated",
+      "src/tracing/consumer_api_deprecated",
     ]
   }
 }
 
 if (!build_with_chromium) {
-  # Client library target.
+  # Client library target exposed to the Android tree.
   # Still in experimental stage and not API stable yet.
   # See "libperfetto_client_example" (in Android.bp.extras) for an example
   # on how to use the Perfetto Client API from the android tree.
@@ -212,13 +211,19 @@
     complete_static_lib = true
     public_deps = [
       "gn:default_deps",
-      "src/tracing",
-      "src/tracing:client_api",
       "src/tracing:platform_posix",
+      "src/tracing/core",
     ]
-    sources = [
-      "include/perfetto/tracing.h",
-    ]
+
+    if (perfetto_build_with_android) {
+      # Android in-tree builds expose only the system backend and don't expose
+      # the in-process backend. This is to save binary size and memory (see
+      # b/148198993).
+      public_deps += [ "src/tracing:client_api_system_backend_only" ]
+    } else {
+      public_deps += [ "src/tracing:client_api" ]
+    }
+    sources = [ "include/perfetto/tracing.h" ]
     assert_no_deps = [ "//gn:protobuf_lite" ]
   }
 }
@@ -233,7 +238,11 @@
     deps = [
       "src/trace_processor:export_json",
       "src/trace_processor:storage_minimal",
-      "src/tracing",
+      "src/tracing:client_api",
+      "src/tracing/core",
+
+      # TODO(eseckler): Create a platform for chrome and hook it up somehow.
+      "src/tracing:platform_fake",
     ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
@@ -241,6 +250,7 @@
       "include/perfetto/ext/trace_processor:export_json",
       "include/perfetto/ext/tracing/core",
       "include/perfetto/trace_processor:storage",
+      "include/perfetto/tracing",
       "protos/perfetto/common:zero",
       "protos/perfetto/trace:zero",
       "protos/perfetto/trace/chrome:zero",
@@ -248,16 +258,19 @@
       "protos/perfetto/trace/profiling:zero",
       "protos/perfetto/trace/track_event:zero",
     ]
+    if (enable_perfetto_ipc) {
+      deps += [
+        "src/tracing/ipc/producer",
+        "src/tracing/ipc/service",
+      ]
+      public_deps += [ "include/perfetto/ext/tracing/ipc:ipc" ]
+    }
   }
   component("libtrace_processor") {
     public_configs = [ "gn:public_config" ]
-    deps = [
-      "src/trace_processor:lib",
-    ]
+    deps = [ "src/trace_processor:lib" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
-    public_deps = [
-      "include/perfetto/trace_processor",
-    ]
+    public_deps = [ "include/perfetto/trace_processor" ]
   }
 }
diff --git a/bazel/proto_gen.bzl b/bazel/proto_gen.bzl
index 6886a28..14e7e75 100644
--- a/bazel/proto_gen.bzl
+++ b/bazel/proto_gen.bzl
@@ -71,7 +71,7 @@
         plugin_deps += [ctx.executable.plugin]
     else:
         arguments += [
-            "--cpp_out=" + out_dir,
+            "--cpp_out=lite=true:" + out_dir,
         ]
 
     arguments += [src.path for src in proto_src]
diff --git a/bazel/rules.bzl b/bazel/rules.bzl
index 7fca94d..498f34a 100644
--- a/bazel/rules.bzl
+++ b/bazel/rules.bzl
@@ -22,7 +22,9 @@
 def default_cc_args():
     return {
         "deps": PERFETTO_CONFIG.deps.build_config,
-        "copts": [],
+        "copts": [
+            "-Wno-pragma-system-header-outside-header",
+        ],
         "includes": ["include"],
         "linkopts": select({
             "@perfetto//bazel:os_linux": ["-ldl", "-lrt", "-lpthread"],
diff --git a/bazel/standalone/perfetto_cfg.bzl b/bazel/standalone/perfetto_cfg.bzl
index 1e8f6d0..d441c06 100644
--- a/bazel/standalone/perfetto_cfg.bzl
+++ b/bazel/standalone/perfetto_cfg.bzl
@@ -55,6 +55,13 @@
         sqlite = [],
     ),
 
+    # Allow Bazel embedders to change the visibility of "public" targets.
+    # This variable has been introduced to limit the change to Bazel and avoid
+    # making the targets fully public in the google internal tree.
+    public_visibility = [
+        "//visibility:public",
+    ],
+
     # Allow Bazel embedders to change the visibility of the proto targets.
     # This variable has been introduced to limit the change to Bazel and avoid
     # making the targets public in the google internal tree.
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 6215959..a0d5f33 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -96,12 +96,8 @@
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":googletest_config" ]
   all_dependent_configs = [ ":googletest_config" ]
-  sources = [
-    "googletest/googletest/src/gtest-all.cc",
-  ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  sources = [ "googletest/googletest/src/gtest-all.cc" ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("gtest_main") {
@@ -109,12 +105,8 @@
   testonly = true
   configs -= [ "//gn/standalone:extra_warnings" ]
   configs += [ ":googletest_config" ]
-  sources = [
-    "googletest/googletest/src/gtest_main.cc",
-  ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  sources = [ "googletest/googletest/src/gtest_main.cc" ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("gmock") {
@@ -124,12 +116,8 @@
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":googletest_config" ]
   all_dependent_configs = [ ":googletest_config" ]
-  sources = [
-    "googletest/googlemock/src/gmock-all.cc",
-  ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  sources = [ "googletest/googlemock/src/gmock-all.cc" ]
+  deps = [ "//gn:default_deps" ]
 }
 
 # This config is applied to the autogenerated .pb.{cc,h} files in
@@ -257,9 +245,7 @@
   configs -= [ "//gn/standalone:extra_warnings" ]
   configs += [ ":protobuf_config" ]
   public_configs = [ ":protobuf_gen_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("protobuf_full") {
@@ -575,9 +561,7 @@
       ":protoc_lib",
       "//gn:default_deps",
     ]
-    sources = [
-      "protobuf/src/google/protobuf/compiler/main.cc",
-    ]
+    sources = [ "protobuf/src/google/protobuf/compiler/main.cc" ]
     configs -= [ "//gn/standalone:extra_warnings" ]
   }
 }  # host_toolchain
@@ -661,9 +645,7 @@
       ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
     ]
-    deps = [
-      ":libunwind",
-    ]
+    deps = [ ":libunwind" ]
   }
 
   if (custom_libcxx_is_static) {
@@ -679,8 +661,10 @@
       "libcxx/src/algorithm.cpp",
       "libcxx/src/any.cpp",
       "libcxx/src/bind.cpp",
+      "libcxx/src/charconv.cpp",
       "libcxx/src/chrono.cpp",
       "libcxx/src/condition_variable.cpp",
+      "libcxx/src/condition_variable_destructor.cpp",
       "libcxx/src/debug.cpp",
       "libcxx/src/exception.cpp",
       "libcxx/src/functional.cpp",
@@ -691,6 +675,7 @@
       "libcxx/src/locale.cpp",
       "libcxx/src/memory.cpp",
       "libcxx/src/mutex.cpp",
+      "libcxx/src/mutex_destructor.cpp",
       "libcxx/src/new.cpp",
       "libcxx/src/optional.cpp",
       "libcxx/src/random.cpp",
@@ -718,9 +703,14 @@
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
     ]
     defines = [ "_LIBCPP_BUILDING_LIBRARY" ]
-    deps = [
-      ":libc++abi",
-    ]
+    if ((is_linux || is_android) && (is_asan || is_tsan || is_msan)) {
+      # In {a,t,m}san configurations, operator new and operator delete will be
+      # provided by the sanitizer runtime library.  Since libc++ defines these
+      # symbols with weak linkage, and the *san runtime uses strong linkage, it
+      # should technically be OK to omit this, but it's added to be explicit.
+      defines += [ "_LIBCPP_DISABLE_NEW_DELETE_DEFINITIONS" ]
+    }
+    deps = [ ":libc++abi" ]
   }
 }  # if (use_custom_libcxx)
 
@@ -774,9 +764,7 @@
   public_configs = [ ":benchmark_config" ]
   all_dependent_configs = [ ":benchmark_config" ]
   configs -= [ "//gn/standalone:extra_warnings" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 # On Linux/Android use libbacktrace in debug builds for better stacktraces.
@@ -810,9 +798,7 @@
     ]
     configs -= [ "//gn/standalone:extra_warnings" ]
     public_configs = [ ":libbacktrace_config" ]
-    deps = [
-      "//gn:default_deps",
-    ]
+    deps = [ "//gn:default_deps" ]
   }
 }
 
@@ -834,6 +820,7 @@
     "-DSQLITE_TEMP_STORE=3",
     "-DSQLITE_OMIT_LOAD_EXTENSION",
     "-DSQLITE_OMIT_RANDOMNESS",
+    "-DSQLITE_OMIT_AUTOINIT",
   ]
 }
 
@@ -847,17 +834,13 @@
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":sqlite_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("sqlite_shell") {
   visibility = _buildtools_visibility
   testonly = true
-  sources = [
-    "sqlite/shell.c",
-  ]
+  sources = [ "sqlite/shell.c" ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   deps = [
     ":sqlite",
@@ -912,9 +895,7 @@
     "-Wno-empty-body",
     "-Wno-enum-conversion",
   ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("zlib") {
@@ -939,9 +920,7 @@
   configs -= [ "//gn/standalone:extra_warnings" ]
   cflags = []
   public_configs = [ ":zlib_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 config("zlib_config") {
@@ -973,11 +952,13 @@
   ]
   sources = [
     "android-core/base/file.cpp",
+    "android-core/base/liblog_symbols.cpp",
     "android-core/base/logging.cpp",
     "android-core/base/stringprintf.cpp",
     "android-core/base/strings.cpp",
     "android-core/base/threads.cpp",
-    "android-core/demangle/Demangler.cpp",
+    "android-core/liblog/fake_log_device.cpp",
+    "android-core/liblog/logger_write.cpp",
     "android-core/libunwindstack/ArmExidx.cpp",
     "android-core/libunwindstack/DwarfCfa.cpp",
     "android-core/libunwindstack/DwarfEhFrameWithHdr.cpp",
@@ -1003,7 +984,6 @@
     "android-core/libunwindstack/RegsX86_64.cpp",
     "android-core/libunwindstack/Symbols.cpp",
     "android-core/libunwindstack/Unwinder.cpp",
-    "android-core/libunwindstack/tests/LogFake.cpp",
   ]
   if (current_cpu == "x86") {
     sources += [ "android-core/libunwindstack/AsmGetRegsX86.S" ]
@@ -1015,10 +995,19 @@
     "//gn/standalone:c++11",
     "//gn/standalone:visibility_hidden",
   ]
+  cflags = [ "-DFAKE_LOG_DEVICE=1" ]
   configs += [ "//gn/standalone:c++17" ]
   public_configs = [ ":libunwindstack_config" ]
 }
 
+config("bionic_kernel_uapi_headers") {
+  visibility = _buildtools_visibility
+  cflags = [
+    "-isystem",
+    rebase_path("bionic/libc/kernel", root_build_dir),
+  ]
+}
+
 config("jsoncpp_config") {
   visibility = _buildtools_visibility
   cflags = [
@@ -1041,9 +1030,7 @@
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":jsoncpp_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 config("linenoise_config") {
@@ -1066,9 +1053,7 @@
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":linenoise_config" ]
   cflags = [ "-Wno-tautological-unsigned-zero-compare" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 if (use_libfuzzer) {
@@ -1103,8 +1088,6 @@
       "libfuzzer/FuzzerUtilPosix.cpp",
       "libfuzzer/FuzzerUtilWindows.cpp",
     ]
-    deps = [
-      "//gn:default_deps",
-    ]
+    deps = [ "//gn:default_deps" ]
   }
 }
diff --git a/docs/java-hprof.md b/docs/java-hprof.md
new file mode 100644
index 0000000..8a3e815
--- /dev/null
+++ b/docs/java-hprof.md
@@ -0,0 +1,52 @@
+# Java Heap Profiling
+
+**Java Heap Profiling requires Android 11.**
+
+Java Heap Profiling allows you to capture a snapshot of the memory use of
+objects managed by ART (Android RunTime). This allows to debug situations
+where a lot of memory is used on the managed heap.
+
+## Quickstart
+
+To grab a profile from your device, run the following command, substituting
+`YOUR_APP_NAME` with the name of the app you want to profile.
+
+```
+ echo 'buffers {
+  size_kb: 100024
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "android.java_hprof"
+    java_hprof_config {
+      process_cmdline: "YOUR_APP_NAME"
+    }
+  }
+}
+
+duration_ms: 10000
+write_into_file: true
+' | adb shell perfetto -c - --out /data/misc/perfetto-traces/profile --txt
+```
+
+Then, pull the data onto your machine.
+
+```
+adb pull /data/misc/perfetto-traces/profile some/path
+```
+
+## Viewing the data
+
+Upload the trace to the [Perfetto UI](https://ui.perfetto.dev) and click on
+diamond marker that shows.
+
+This will present a flamegraph of the memory attributed to the shortest path
+to a garbage-collection root. In general an object is reachable by many paths,
+we only show the shortest as that reduces the complexity of the data displayed
+and is generally the highest-signal.
+
+We aggregate the paths per class name, so if there are two `Foo` objects that
+each retain a `String`, we will show one element for `String` as a child of
+one `Foo`.
diff --git a/docs/metrics.md b/docs/metrics.md
index 6f4079b..27e9c0a 100644
--- a/docs/metrics.md
+++ b/docs/metrics.md
@@ -117,7 +117,6 @@
 gives:
 ```protobuf
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/docs/toc.md b/docs/toc.md
index 3aac647..ddc2992 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -8,6 +8,7 @@
   * [Advanced trace config](trace-config.md)
   * [Running in detached mode](detached-mode.md)
   * [Native Heap Profiling](heapprofd.md)
+  * [Java Heap Profiling](java-hprof.md)
 * Trace analysis
   * [Trace processor](trace-processor.md)
   * [Trace-based metrics](metrics.md)
diff --git a/docs/trace-processor.md b/docs/trace-processor.md
index cea8846..532056b 100644
--- a/docs/trace-processor.md
+++ b/docs/trace-processor.md
@@ -8,8 +8,10 @@
 through SQL queries. The trace processor is used:
 * By the [Perfetto UI](https://ui.perfetto.dev/), in the form of a
   Web Assembly module.
-* Standalone, using the `trace_processor_shell` target
-  (`ninja -C out/xxx trace_processor_shell`).
+* Standalone:
+  * using the [prebuilt](http://get.perfetto.dev/trace_processor) binaries.
+  * using the `trace_processor_shell` target from source
+    (`ninja -C out/xxx trace_processor_shell`).
 * In internal pipelines for batch processing.
 
 Supported input formats:
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index cb35cd0..b00f447 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -85,6 +85,7 @@
     "PERFETTO_TP_JSON_IMPORT=$enable_perfetto_trace_processor_json_import",
     "PERFETTO_TP_FUCHSIA=$enable_perfetto_trace_processor_fuchsia",
     "PERFETTO_LOCAL_SYMBOLIZER=$perfetto_local_symbolizer",
+    "PERFETTO_ZLIB=$enable_perfetto_zlib",
   ]
 
   rel_out_path = rebase_path(gen_header_path, "$root_build_dir")
@@ -95,18 +96,14 @@
     "{{response_file_name}}",
   ]
 
-  outputs = [
-    gen_header_path,
-  ]
+  outputs = [ gen_header_path ]
 }
 
 # All targets should depend on this target to inherit the right flags and
 # include directories.
 group("default_deps") {
   public_configs = [ ":default_config" ]
-  deps = [
-    ":gen_buildflags",
-  ]
+  deps = [ ":gen_buildflags" ]
   if (perfetto_build_standalone) {
     public_deps = [
       "//gn/standalone/libc++:deps",
@@ -138,6 +135,12 @@
 
     # For perfetto_build_flags.h
     buildflag_gen_dir_,
+
+    # For generated files (proto libraries etc). We add the directory here
+    # because we stop propagation of the configs for individual proto libraries
+    # to avoid duplicate include directory command line flags in compiler
+    # invocations, see proto_library.gni, crbug.com/1043279, crbug.com/gn/142.
+    "$root_gen_dir/$perfetto_root_path",
   ]
 }
 
@@ -177,33 +180,25 @@
   testonly = true
 
   if (perfetto_root_path == "//") {
-    public_deps = [
-      "//buildtools:gtest_main",
-    ]
+    public_deps = [ "//buildtools:gtest_main" ]
   } else if (build_with_chromium) {
-    public_deps = [
-      "//base/test:run_all_unittests",
-    ]
+    public_deps = [ "//base/test:run_all_unittests" ]
   } else {
-    public_deps = [
-      "//testing/gtest:gtest_main",
-    ]
+    public_deps = [ "//testing/gtest:gtest_main" ]
   }
 }
 
 # Full protobuf is just for host tools .No binary shipped on device should
 # depend on this.
 whitelisted_protobuf_full_deps = [
-  "../tools/*",
   "../src/ipc/protoc_plugin:*",
   "../src/protozero/protoc_plugin:*",
   "../src/trace_processor:trace_processor_shell",
+  "../tools/*",
 ]
 
 group("protoc") {
-  public_deps = [
-    "${perfetto_protobuf_target_prefix}:protoc($host_toolchain)",
-  ]
+  public_deps = [ "${perfetto_protobuf_target_prefix}:protoc($host_toolchain)" ]
 }
 
 # protoc compiler library, it's used for building protoc plugins and by
@@ -211,25 +206,19 @@
 group("protoc_lib") {
   visibility = whitelisted_protobuf_full_deps
   if (current_toolchain == host_toolchain) {
-    public_deps = [
-      "${perfetto_protobuf_target_prefix}:protoc_lib",
-    ]
+    public_deps = [ "${perfetto_protobuf_target_prefix}:protoc_lib" ]
   }
 }
 
 group("protobuf_full") {
   visibility = whitelisted_protobuf_full_deps
   if (current_toolchain == host_toolchain) {
-    public_deps = [
-      "${perfetto_protobuf_target_prefix}:protobuf_full",
-    ]
+    public_deps = [ "${perfetto_protobuf_target_prefix}:protobuf_full" ]
   }
 }
 
 group("protobuf_lite") {
-  public_deps = [
-    "${perfetto_protobuf_target_prefix}:protobuf_lite",
-  ]
+  public_deps = [ "${perfetto_protobuf_target_prefix}:protobuf_lite" ]
 }
 
 # The Google C++ Benchmark library.
@@ -237,9 +226,7 @@
 if (enable_perfetto_benchmarks) {
   group("benchmark") {
     testonly = true
-    public_deps = [
-      "//buildtools:benchmark",
-    ]
+    public_deps = [ "//buildtools:benchmark" ]
   }
 }
 
@@ -247,22 +234,18 @@
 # standalone debug builds.
 if (perfetto_build_standalone && (is_linux || is_android)) {
   group("libbacktrace") {
-    public_deps = [
-      "//buildtools:libbacktrace",
-    ]
+    public_deps = [ "//buildtools:libbacktrace" ]
   }
 }
 
-group("sqlite") {
-  if (perfetto_root_path == "//") {
-    public_deps = [
-      "//buildtools:sqlite",
-    ]
-  } else {
-    public_deps = [
-      "//third_party/sqlite:sqlite",
-    ]
-    public_configs = [ ":sqlite_third_party_include_path" ]
+if (enable_perfetto_trace_processor_sqlite) {
+  group("sqlite") {
+    if (perfetto_root_path == "//") {
+      public_deps = [ "//buildtools:sqlite" ]
+    } else {
+      public_deps = [ "//third_party/sqlite:sqlite" ]
+      public_configs = [ ":sqlite_third_party_include_path" ]
+    }
   }
 }
 
@@ -274,13 +257,9 @@
   group("jsoncpp") {
     if (perfetto_root_path == "//") {
       public_configs = [ "//buildtools:jsoncpp_config" ]
-      public_deps = [
-        "//buildtools:jsoncpp",
-      ]
+      public_deps = [ "//buildtools:jsoncpp" ]
     } else {
-      public_deps = [
-        "//third_party/jsoncpp:jsoncpp",
-      ]
+      public_deps = [ "//third_party/jsoncpp:jsoncpp" ]
     }
   }
 }
@@ -289,35 +268,34 @@
   # Used by the trace_processor_shell for REPL history.
   # Only available in standalone builds.
   group("linenoise") {
-    public_deps = [
-      "//buildtools:linenoise",
-    ]
+    public_deps = [ "//buildtools:linenoise" ]
   }
 }  # if (enable_perfetto_trace_processor_linenoise)
 
 # Only used by src/profiling in standalone and android builds.
-if (enable_perfetto_heapprofd) {
+if (enable_perfetto_heapprofd || enable_perfetto_traced_perf) {
   group("libunwindstack") {
     public_configs = [ "//buildtools:libunwindstack_config" ]
-    public_deps = [
-      "//buildtools:libunwindstack",
-    ]
+    public_deps = [ "//buildtools:libunwindstack" ]
+  }
+}
+
+# Used by src/profiling/perf for perf_regs.h.
+if (enable_perfetto_traced_perf) {
+  group("bionic_kernel_uapi_headers") {
+    public_configs = [ "//buildtools:bionic_kernel_uapi_headers" ]
   }
 }
 
 # Zlib is used both by trace_processor and by perfetto_cmd.
-if (enable_perfetto_trace_processor || enable_perfetto_platform_services) {
+if (enable_perfetto_zlib) {
   group("zlib") {
     if (perfetto_root_path == "//") {
       public_configs = [ "//buildtools:zlib_config" ]
-      public_deps = [
-        "//buildtools:zlib",
-      ]
+      public_deps = [ "//buildtools:zlib" ]
     } else {
       public_configs = [ "//third_party/zlib:zlib_config" ]
-      public_deps = [
-        "//third_party/zlib",
-      ]
+      public_deps = [ "//third_party/zlib" ]
     }
   }
 }
@@ -326,8 +304,6 @@
 if (enable_perfetto_fuzzers && use_libfuzzer) {
   group("libfuzzer") {
     assert(perfetto_root_path == "//")
-    public_deps = [
-      "//buildtools:libfuzzer",
-    ]
+    public_deps = [ "//buildtools:libfuzzer" ]
   }
 }
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index b479435..e6ed051 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -66,6 +66,10 @@
   build_with_chromium = false
 }
 
+if (!defined(is_nacl)) {
+  is_nacl = false
+}
+
 declare_args() {
   # The Android blueprint file generator set this to true (as well as
   # is_perfetto_build_generator). This is just about being built in the
@@ -135,7 +139,7 @@
   # system backend in the client library.
   # This includes building things that rely on POSIX sockets, this places
   # limitations on the supported operating systems.
-  enable_perfetto_ipc = (is_android || is_linux || is_mac) &&
+  enable_perfetto_ipc = !is_win && !is_fuchsia && !is_nacl &&
                         (perfetto_build_standalone ||
                          perfetto_build_with_android || build_with_chromium)
 
@@ -149,7 +153,11 @@
 
   # Build the perf event profiler (traced_perf).
   # TODO(b/144281346): under development.
-  enable_perfetto_traced_perf = perfetto_build_with_android
+  # TODO(rsavitski): figure out how to make the android-core dependencies build
+  # under gcc (_Atomic and other issues).
+  enable_perfetto_traced_perf =
+      perfetto_build_with_android ||
+      (perfetto_build_standalone && is_clang && is_linux)
 
   # The Trace Processor: offline analytical engine to process traces and compute
   # metrics using a SQL engine.
@@ -200,6 +208,11 @@
   perfetto_verbose_logs_enabled =
       !build_with_chromium || perfetto_force_dlog == "on"
 
+  # Enables the SQL query layer of trace processor.
+  enable_perfetto_trace_processor_sqlite =
+      enable_perfetto_trace_processor &&
+      (build_with_chromium || !perfetto_build_with_embedder)
+
   # Enables the optional SQLite percentile module.
   enable_perfetto_trace_processor_percentile =
       enable_perfetto_trace_processor && perfetto_build_standalone
@@ -223,6 +236,11 @@
   # Further per-OS conditionals are applied in gn/BUILD.gn.
   enable_perfetto_trace_processor_httpd =
       enable_perfetto_trace_processor && perfetto_build_standalone
+
+  # Enables Zlib support. This is used both by the "perfetto" cmdline client
+  # (for compressing traces) and by trace processor (for compressed traces).
+  enable_perfetto_zlib =
+      enable_perfetto_trace_processor || enable_perfetto_platform_services
 }
 
 declare_args() {
@@ -232,10 +250,12 @@
       !(build_with_chromium && is_android)
 
   # Enables the trace_to_text tool.
-  enable_perfetto_tools_trace_to_text = enable_perfetto_tools
+  enable_perfetto_tools_trace_to_text =
+      enable_perfetto_tools && enable_perfetto_trace_processor_sqlite
 
   # Allows to build the UI (TypeScript/ HTML / WASM)
-  enable_perfetto_ui = perfetto_build_standalone
+  enable_perfetto_ui =
+      perfetto_build_standalone && enable_perfetto_trace_processor_sqlite
 }
 
 # +---------------------------------------------------------------------------+
diff --git a/gn/perfetto_benchmarks.gni b/gn/perfetto_benchmarks.gni
index 122c6ff..2abd944 100644
--- a/gn/perfetto_benchmarks.gni
+++ b/gn/perfetto_benchmarks.gni
@@ -20,7 +20,8 @@
   "src/traced/probes/ftrace:benchmarks",
   "src/trace_processor/containers:benchmarks",
   "src/trace_processor/tables:benchmarks",
-  "src/tracing:benchmarks",
+  "src/tracing/core:benchmarks",
+  "src/traced/probes/ftrace/kallsyms:benchmarks",
   "test:benchmark_main",
   "test:end_to_end_benchmarks",
 ]
diff --git a/gn/perfetto_fuzzers.gni b/gn/perfetto_fuzzers.gni
index a82b74f..0a5dbfa 100644
--- a/gn/perfetto_fuzzers.gni
+++ b/gn/perfetto_fuzzers.gni
@@ -18,7 +18,7 @@
   "gn:default_deps",
   "src/ipc:buffered_frame_deserializer_fuzzer",
   "src/protozero:protozero_decoder_fuzzer",
-  "src/tracing:packet_stream_validator_fuzzer",
+  "src/tracing/core:packet_stream_validator_fuzzer",
   "src/trace_processor:trace_processor_fuzzer",
   "src/traced/probes/ftrace:cpu_reader_fuzzer",
   "test:end_to_end_shared_memory_fuzzer",
diff --git a/gn/perfetto_host_executable.gni b/gn/perfetto_host_executable.gni
index 55edc9a..1d12d92 100644
--- a/gn/perfetto_host_executable.gni
+++ b/gn/perfetto_host_executable.gni
@@ -36,27 +36,19 @@
       # (See crbug.com/1002599).
       group(target_name) {
         testonly = _testonly
-        deps = [
-          _host_target,
-        ]
+        deps = [ _host_target ]
       }
     } else {
       copy(target_name) {
         testonly = _testonly
-        deps = [
-          _host_target,
-        ]
+        deps = [ _host_target ]
         _host_out_dir = get_label_info(_host_target, "root_out_dir")
         _extension = ""
         if (host_os == "win") {
           _extension = ".exe"
         }
-        sources = [
-          "$_host_out_dir/$target_name${_extension}",
-        ]
-        outputs = [
-          "$root_out_dir/$target_name${_extension}",
-        ]
+        sources = [ "$_host_out_dir/$target_name${_extension}" ]
+        outputs = [ "$root_out_dir/$target_name${_extension}" ]
       }
     }
   }
diff --git a/gn/perfetto_integrationtests.gni b/gn/perfetto_integrationtests.gni
index 19b5189..d2b0181 100644
--- a/gn/perfetto_integrationtests.gni
+++ b/gn/perfetto_integrationtests.gni
@@ -17,7 +17,7 @@
 perfetto_integrationtests_targets = [
   "gn:default_deps",
   "gn:gtest_main",
-  "src/tracing:client_api_integrationtests",
+  "src/tracing/test:client_api_integrationtests",
 ]
 
 if (enable_perfetto_platform_services) {
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index ead0c8b..84467e6 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -19,10 +19,14 @@
   "gn:gtest_main",
   "src/base:unittests",
   "src/protozero:unittests",
-  "src/tracing:unittests",
+  "src/tracing/core:unittests",
   "src/profiling:unittests",
 ]
 
+if (enable_perfetto_ipc) {
+  perfetto_unittests_targets += [ "src/tracing/test:tracing_integration_test" ]
+}
+
 if (enable_perfetto_tools && current_toolchain == host_toolchain) {
   perfetto_unittests_targets += [ "tools/ftrace_proto_gen:unittests" ]
 }
@@ -36,7 +40,10 @@
 }
 
 if (enable_perfetto_ipc) {
-  perfetto_unittests_targets += [ "src/ipc:unittests" ]
+  perfetto_unittests_targets += [
+    "src/tracing/ipc:unittests",
+    "src/ipc:unittests",
+  ]
 }
 
 if (enable_perfetto_platform_services) {
@@ -45,6 +52,7 @@
     "src/traced/probes:unittests",
     "src/traced/probes/filesystem:unittests",
     "src/traced/probes/ftrace:unittests",
+    "src/traced/probes/ftrace/kallsyms:unittests",
     "src/traced/service:unittests",
   ]
 }
@@ -61,8 +69,9 @@
 }
 
 if (enable_perfetto_trace_processor) {
-  perfetto_unittests_targets += [
-    "src/trace_processor:unittests",
-    "src/trace_processor/metrics:unittests",
-  ]
+  perfetto_unittests_targets += [ "src/trace_processor:unittests" ]
+
+  if (enable_perfetto_trace_processor_sqlite) {
+    perfetto_unittests_targets += [ "src/trace_processor/metrics:unittests" ]
+  }
 }
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index 1a59de7..d92c669 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -90,6 +90,7 @@
                              "testonly",
                              "visibility",
                              "generate_descriptor",
+                             "propagate_imports_configs",
                            ])
   }
 }
@@ -133,6 +134,7 @@
                              "testonly",
                              "visibility",
                              "generate_descriptor",
+                             "propagate_imports_configs",
                            ])
   }
 }
@@ -147,7 +149,8 @@
         "$perfetto_root_path/src/ipc/protoc_plugin:ipc_plugin"
     generator_plugin_suffix = ".ipc"
     deps = [
-      "$perfetto_root_path/src/ipc",
+      "$perfetto_root_path/gn:default_deps",
+      "$perfetto_root_path/src/ipc:common",
     ]
     if (defined(invoker.deps)) {
       deps += invoker.deps
@@ -163,6 +166,7 @@
                              "sources",
                              "testonly",
                              "visibility",
+                             "propagate_imports_configs",
                            ])
   }
 }
@@ -193,6 +197,11 @@
   ]
   expansion_token = "@TYPE@"
 
+  # gn:public_config propagates the gen dir as include directory. We
+  # disable the proto_library's public_config to avoid duplicate include
+  # directory command line flags (crbug.com/1043279, crbug.com/gn/142).
+  propagate_imports_configs_ = false
+
   foreach(gen_type, proto_generators) {
     target_name_ = string_replace(target_name, expansion_token, gen_type)
 
@@ -210,6 +219,7 @@
         proto_out_dir = proto_path
         generator_plugin_options = "wrapper_namespace=pbzero"
         deps = deps_
+        propagate_imports_configs = propagate_imports_configs_
         forward_variables_from(invoker, vars_to_forward)
       }
     } else if (gen_type == "cpp") {
@@ -218,6 +228,7 @@
         proto_out_dir = proto_path
         generator_plugin_options = "wrapper_namespace=gen"
         deps = deps_
+        propagate_imports_configs = propagate_imports_configs_
         forward_variables_from(invoker, vars_to_forward)
       }
     } else if (gen_type == "ipc") {
@@ -227,6 +238,7 @@
         proto_out_dir = proto_path
         generator_plugin_options = "wrapper_namespace=gen"
         deps = deps_ + [ ":$cpp_target_name_" ]
+        propagate_imports_configs = propagate_imports_configs_
         forward_variables_from(invoker, vars_to_forward)
       }
     } else if (gen_type == "lite") {
@@ -235,6 +247,8 @@
         proto_out_dir = proto_path
         generate_python = false
         deps = deps_
+        cc_generator_options = "lite=true:"
+        propagate_imports_configs = propagate_imports_configs_
         forward_variables_from(invoker, vars_to_forward)
       }
     } else if (gen_type == "descriptor") {
@@ -247,6 +261,9 @@
         deps = deps_
         forward_variables_from(invoker, vars_to_forward)
       }
+
+      # Not needed for descriptor proto_library target.
+      not_needed([ "propagate_imports_configs_" ])
     } else {
       assert(false, "Invalid 'proto_generators' value.")
     }
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index 0c77eca..387e258 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -16,12 +16,28 @@
 import("//gn/standalone/sanitizers/sanitizers.gni")
 import("//gn/standalone/wasm.gni")
 
+# These warnings have been introduced with the newest version of clang (only in
+# the hermetic build) and are enabled just with -Werror.
+# TODO(primiano): we should look into Wimplicit-int-float-conversion. Seems
+# failing mostly in tests though.
+hermetic_clang_suppressions = [
+  "-Wno-c99-designator",
+  "-Wno-implicit-int-float-conversion",
+]
+
 config("extra_warnings") {
   cflags = [
     "-Wall",
     "-Wextra",
+    "-Wpedantic",
   ]
 
+  # Disable variadic macro warning as we make extensive use of them in trace
+  # processor and client API.
+  if (is_clang) {
+    cflags += [ "-Wno-gnu-zero-variadic-macro-arguments" ]
+  }
+
   # Disable Weverything on fuzzers to avoid breakages when new versions of clang
   # are rolled into OSS-fuzz.
   if (is_clang && !is_fuzzer) {
@@ -32,7 +48,6 @@
       "-Wno-disabled-macro-expansion",
       "-Wno-gnu-include-next",
       "-Wno-gnu-statement-expression",
-      "-Wno-gnu-zero-variadic-macro-arguments",
       "-Wno-padded",
       "-Wno-reserved-id-macro",
       "-Wno-unknown-sanitizers",
@@ -90,6 +105,12 @@
     ]
   }
 
+  if (is_hermetic_clang && is_linux && !is_wasm) {
+    cflags += hermetic_clang_suppressions
+  } else {
+    not_needed([ "hermetic_clang_suppressions" ])
+  }
+
   if (is_lto) {
     cflags += [ "-flto=full" ]
     ldflags += [ "-flto=full" ]
@@ -262,8 +283,6 @@
   generated_header = "${root_gen_dir}/perfetto_version.gen.h"
   args = [ rebase_path(generated_header, root_build_dir) ]
   inputs = []
-  outputs = [
-    generated_header,
-  ]
+  outputs = [ generated_header ]
   public_configs = [ ":gen_include_path" ]
 }
diff --git a/gn/standalone/BUILDCONFIG.gn b/gn/standalone/BUILDCONFIG.gn
index d980a05..3668c0f 100644
--- a/gn/standalone/BUILDCONFIG.gn
+++ b/gn/standalone/BUILDCONFIG.gn
@@ -36,10 +36,11 @@
 is_linux_host = host_os == "linux"
 is_mac = current_os == "mac"
 
-# Building with Windows/Fuchsia is currently only supported in the Chromium
+# Building with Windows/Fuchsia/nacl is currently only supported in the Chromium
 # tree so always set this to false.
 is_win = false
 is_fuchsia = false
+is_nacl = false
 
 if (target_cpu == "") {
   target_cpu = host_cpu
diff --git a/gn/standalone/libc++/BUILD.gn b/gn/standalone/libc++/BUILD.gn
index a8ccba1..8590f85 100644
--- a/gn/standalone/libc++/BUILD.gn
+++ b/gn/standalone/libc++/BUILD.gn
@@ -42,8 +42,6 @@
 
 group("deps") {
   if (use_custom_libcxx) {
-    public_deps = [
-      "//buildtools:libc++",
-    ]
+    public_deps = [ "//buildtools:libc++" ]
   }
 }
diff --git a/gn/standalone/proto_library.gni b/gn/standalone/proto_library.gni
index 0180b2b..212ea42 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -138,9 +138,13 @@
       rebase_path(proto_in_dir, root_build_dir),
     ]
     if (generate_cc) {
+      cc_generator_options_ = ""
+      if (defined(invoker.cc_generator_options)) {
+        cc_generator_options_ = invoker.cc_generator_options
+      }
       args += [
         "--cpp_out",
-        rel_cc_out_dir,
+        cc_generator_options_ + rel_cc_out_dir,
       ]
     }
     if (generate_descriptor != "") {
@@ -170,13 +174,9 @@
 
     args += rebase_path(proto_sources, root_build_dir)
 
-    inputs = [
-      protoc_path,
-    ]
+    inputs = [ protoc_path ]
 
-    deps = [
-      protoc_label,
-    ]
+    deps = [ protoc_label ]
 
     # TODO(hjd): Avoid adding to deps here this.
     # When we generate BUILD files we need find the transitive proto,
@@ -226,16 +226,24 @@
         ":$config_name",
       ]
 
+      # By default, propagate the config for |include_dirs| to dependent
+      # targets, so that public imports can be resolved to corresponding header
+      # files. In some cases, the embedder target handles include directory
+      # propagation itself, e.g. via a common config.
+      propagate_imports_configs = !defined(invoker.propagate_imports_configs) ||
+                                  invoker.propagate_imports_configs
+      if (propagate_imports_configs) {
+        public_configs += [ ":$config_name" ]
+      } else {
+        configs += [ ":$config_name" ]
+      }
+
       # Use protobuf_full only for tests.
       if (defined(invoker.use_protobuf_full) &&
           invoker.use_protobuf_full == true) {
-        deps = [
-          "//gn:protobuf_full",
-        ]
+        deps = [ "//gn:protobuf_full" ]
       } else if (generate_cc) {
-        deps = [
-          "//gn:protobuf_lite",
-        ]
+        deps = [ "//gn:protobuf_lite" ]
       } else {
         deps = []
       }
diff --git a/gn/standalone/sanitizers/BUILD.gn b/gn/standalone/sanitizers/BUILD.gn
index 8a5253d..c015d3e 100644
--- a/gn/standalone/sanitizers/BUILD.gn
+++ b/gn/standalone/sanitizers/BUILD.gn
@@ -20,21 +20,15 @@
   if (using_sanitizer) {
     public_configs = [ ":sanitizers_ldflags" ]
     if (is_android && sanitizer_lib != "" && !sanitizer_lib_dir_is_static) {
-      deps = [
-        ":copy_sanitizer_lib",
-      ]
+      deps = [ ":copy_sanitizer_lib" ]
     }
   }
 }
 
 if (is_android && sanitizer_lib != "" && !sanitizer_lib_dir_is_static) {
   copy("copy_sanitizer_lib") {
-    sources = [
-      "${sanitizer_lib_dir}/lib${sanitizer_lib}.so",
-    ]
-    outputs = [
-      "${root_out_dir}/sanitizer_libs/lib${sanitizer_lib}.so",
-    ]
+    sources = [ "${sanitizer_lib_dir}/lib${sanitizer_lib}.so" ]
+    outputs = [ "${root_out_dir}/sanitizer_libs/lib${sanitizer_lib}.so" ]
   }
 }
 
diff --git a/gn/standalone/toolchain/BUILD.gn b/gn/standalone/toolchain/BUILD.gn
index 417d7f4..264d27f 100644
--- a/gn/standalone/toolchain/BUILD.gn
+++ b/gn/standalone/toolchain/BUILD.gn
@@ -146,9 +146,8 @@
       depfile = "{{output}}.d"
       command = "$cc_wrapper $cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} ${extra_cflags} -c {{source}} -o {{output}}"
       depsformat = "gcc"
-      outputs = [
-        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
-      ]
+      outputs =
+          [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
       description = "compile {{source}}"
     }
 
@@ -156,9 +155,8 @@
       depfile = "{{output}}.d"
       command = "$cc_wrapper $cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}  ${extra_cflags} ${extra_cxxflags} -c {{source}} -o {{output}}"
       depsformat = "gcc"
-      outputs = [
-        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
-      ]
+      outputs =
+          [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
       description = "compile {{source}}"
     }
 
@@ -166,9 +164,8 @@
       depfile = "{{output}}.d"
       command = "$cc_wrapper $cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
       depsformat = "gcc"
-      outputs = [
-        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
-      ]
+      outputs =
+          [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
       description = "assemble {{source}}"
     }
 
@@ -181,9 +178,8 @@
         rspfile_content = "{{inputs}}"
         command = "rm -f {{output}} && $ar rcsD {{output}} @$rspfile"
       }
-      outputs = [
-        "{{root_out_dir}}/{{target_output_name}}{{output_extension}}",
-      ]
+      outputs =
+          [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ]
       default_output_extension = ".a"
       output_prefix = "lib"
       description = "link {{output}}"
@@ -198,9 +194,7 @@
       }
 
       command = "$cc_wrapper $cxx $ld_arg -shared {{ldflags}} ${extra_ldflags} {{inputs}} {{solibs}} {{libs}} $rpath -o {{output}}"
-      outputs = [
-        "{{root_out_dir}}/$soname",
-      ]
+      outputs = [ "{{root_out_dir}}/$soname" ]
       output_prefix = "lib"
       default_output_extension = ".so"
       description = "link {{output}}"
@@ -208,9 +202,8 @@
 
     tool("link") {
       command = "$cc_wrapper $cxx $ld_arg {{ldflags}} ${extra_ldflags} {{inputs}} {{solibs}} {{libs}} -o {{output}}"
-      outputs = [
-        "{{root_out_dir}}/{{target_output_name}}{{output_extension}}",
-      ]
+      outputs =
+          [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ]
       description = "link {{output}}"
     }
 
diff --git a/gn/standalone/toolchain/linux_find_llvm.py b/gn/standalone/toolchain/linux_find_llvm.py
index 858185c..3a957fe 100644
--- a/gn/standalone/toolchain/linux_find_llvm.py
+++ b/gn/standalone/toolchain/linux_find_llvm.py
@@ -22,7 +22,7 @@
   for clang in ('clang', 'clang-3.8', 'clang-3.5'):
     if subprocess.call(['which', clang], stdout=devnull, stderr=devnull) != 0:
       continue
-    res = subprocess.check_output([clang, '-print-search-dirs'])
+    res = subprocess.check_output([clang, '-print-search-dirs']).decode("utf-8")
     for line in res.splitlines():
       if not line.startswith('libraries:'):
         continue
@@ -30,11 +30,11 @@
       for lib in libs:
         if '/clang/' not in lib or not os.path.isdir(lib + '/lib'):
           continue
-        print os.path.abspath(lib)
-        print clang
-        print clang.replace('clang', 'clang++')
+        print(os.path.abspath(lib))
+        print(clang)
+        print(clang.replace('clang', 'clang++'))
         return 0
-  print 'Could not find the LLVM lib dir'
+  print('Could not find the LLVM lib dir')
   return 1
 
 
diff --git a/gn/standalone/wasm.gni b/gn/standalone/wasm.gni
index b3a6c1a..bfa46ec 100644
--- a/gn/standalone/wasm.gni
+++ b/gn/standalone/wasm.gni
@@ -116,12 +116,8 @@
     # outputs also the .wasm file", so we can depend on that in copy() targets.
     action("${_lib_name}.wasm") {
       inputs = []
-      deps = [
-        ":${_lib_name}.js",
-      ]
-      outputs = [
-        "$root_out_dir/$_lib_name.wasm",
-      ]
+      deps = [ ":${_lib_name}.js" ]
+      outputs = [ "$root_out_dir/$_lib_name.wasm" ]
       if (is_debug) {
         outputs += [ "$root_out_dir/$_lib_name.wasm.map" ]
       }
@@ -130,12 +126,8 @@
     }
 
     copy("${_lib_name}.d.ts") {
-      sources = [
-        "//gn/standalone/wasm_typescript_declaration.d.ts",
-      ]
-      outputs = [
-        "$root_out_dir/$_lib_name.d.ts",
-      ]
+      sources = [ "//gn/standalone/wasm_typescript_declaration.d.ts" ]
+      outputs = [ "$root_out_dir/$_lib_name.d.ts" ]
     }
   } else {  # is_wasm
     not_needed(invoker, "*")
diff --git a/gn/standalone/write_ui_dist_file_map.py b/gn/standalone/write_ui_dist_file_map.py
new file mode 100644
index 0000000..8ea5aff
--- /dev/null
+++ b/gn/standalone/write_ui_dist_file_map.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# Copyright (C) 2020 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.
+""" Writes a TypeScript dict that contains SHA256s of the passed files.
+
+The output looks like this:
+{
+  hex_digest: '6c761701c5840483833ffb0bd70ae155b2b3c70e8f667c7bd1f6abc98095930',
+  files: {
+    'frontend_bundle.js': 'sha256-2IVKK/3mEMlDdXNADyK03L1cANKbBpU+xue+vnLOcyo=',
+    'index.html': 'sha256-ZRS1+Xh/dFZeWZi/dz8QMWg/8PYQHNdazsNX2oX8s70=',
+    ...
+  }
+}
+"""
+
+from __future__ import print_function
+
+import argparse
+import base64
+import hashlib
+import multiprocessing
+import os
+import sys
+
+from base64 import b64encode
+
+
+def hash_file(file_path):
+  hasher = hashlib.sha256()
+  with open(file_path, 'rb') as f:
+    for chunk in iter(lambda: f.read(32768), b''):
+      hasher.update(chunk)
+    return file_path, hasher.digest()
+
+
+def hash_list_hex(args):
+  hasher = hashlib.sha256()
+  for arg in args:
+    hasher.update(arg)
+  return hasher.hexdigest()
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--out', help='Path of the output file')
+  parser.add_argument(
+      '--strip', help='Strips the leading path in the generated file list')
+  parser.add_argument('file_list', nargs=argparse.REMAINDER)
+  args = parser.parse_args()
+
+  # Compute the hash of each file (in parallel).
+  pool = multiprocessing.Pool(multiprocessing.cpu_count() * 2)
+  digests = dict(pool.map(hash_file, args.file_list))
+
+  contents = '// __generated_by %s\n' % __file__
+  contents += 'export const UI_DIST_MAP = {\n'
+  contents += '  files: {\n'
+  strip = args.strip + ('' if args.strip[-1] == os.path.sep else os.path.sep)
+  for fname, digest in digests.iteritems():
+    if not fname.startswith(strip):
+      raise Exception('%s must start with %s (--strip arg)' % (fname, strip))
+    fname = fname[len(strip):]
+    # We use b64 instead of hexdigest() because it's handy for handling fetch()
+    # subresource integrity.
+    contents += '    \'%s\': \'sha256-%s\',\n' % (fname, b64encode(digest))
+  contents += '  },\n'
+
+  # Compute the hash of the all resources' hashes.
+  contents += '  hex_digest: \'%s\',\n' % hash_list_hex(digests.values())
+  contents += '};\n'
+
+  with open(args.out + '.tmp', 'w') as fout:
+    fout.write(contents)
+  os.rename(args.out + '.tmp', args.out)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/include/README.md b/include/README.md
index 83a92f7..6edb3ca 100644
--- a/include/README.md
+++ b/include/README.md
@@ -87,8 +87,6 @@
     void OnStop(const StopArgs&) override {}
   };
   ...
-  PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(MyDataSource);
-  ...
   perfetto::DataSourceDescriptor dsd;
   dsd.set_name("my_data_source");
   MyDataSource::Register(dsd);
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index e490551..85819b7 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -19,8 +19,11 @@
     "build_config.h",
     "compiler.h",
     "export.h",
+    "flat_set.h",
     "logging.h",
+    "proc_utils.h",
     "task_runner.h",
+    "thread_utils.h",
     "time.h",
   ]
 }
diff --git a/include/perfetto/base/build_config.h b/include/perfetto/base/build_config.h
index bb641f5..047d897 100644
--- a/include/perfetto/base/build_config.h
+++ b/include/perfetto/base/build_config.h
@@ -31,6 +31,8 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
 #elif defined(__APPLE__)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 1
@@ -38,6 +40,12 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
+// Include TARGET_OS_IPHONE when on __APPLE__ systems.
+#include <TargetConditionals.h>
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 1
+#endif
 #elif defined(__linux__)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 0
@@ -45,6 +53,8 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
 #elif defined(_WIN32)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 0
@@ -52,6 +62,8 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 1
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
 #elif defined(__EMSCRIPTEN__)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 0
@@ -59,6 +71,8 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 1
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
 #elif defined(__Fuchsia__)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 0
@@ -66,6 +80,17 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 1
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
+#elif defined(__native_client__)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 1
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
 #else
 #error OS not supported (see build_config.h)
 #endif
diff --git a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
index 259721b..dbc68b7 100644
--- a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
@@ -39,6 +39,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON_IMPORT() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_FUCHSIA() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX())
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
 
 // clang-format on
 #endif  // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index 7919b4d..ce91468 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -39,6 +39,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON_IMPORT() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_FUCHSIA() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX())
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
 
 // clang-format on
 #endif  // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/ext/base/flat_set.h b/include/perfetto/base/flat_set.h
similarity index 91%
rename from include/perfetto/ext/base/flat_set.h
rename to include/perfetto/base/flat_set.h
index e33fcf7..068ad3c 100644
--- a/include/perfetto/ext/base/flat_set.h
+++ b/include/perfetto/base/flat_set.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAT_SET_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FLAT_SET_H_
+#ifndef INCLUDE_PERFETTO_BASE_FLAT_SET_H_
+#define INCLUDE_PERFETTO_BASE_FLAT_SET_H_
 
 #include <algorithm>
 #include <vector>
@@ -63,14 +63,15 @@
 
   size_t count(T value) const { return find(value) == entries_.end() ? 0 : 1; }
 
-  void insert(T value) {
+  std::pair<iterator, bool> insert(T value) {
     auto entries_end = entries_.end();
     auto it = std::lower_bound(entries_.begin(), entries_end, value);
     if (it != entries_end && *it == value)
-      return;
+      return std::make_pair(it, false);
     // If the value is not found |it| is either end() or the next item strictly
     // greater than |value|. In both cases we want to insert just before that.
-    entries_.insert(it, std::move(value));
+    it = entries_.insert(it, std::move(value));
+    return std::make_pair(it, true);
   }
 
   size_t erase(T value) {
@@ -96,4 +97,4 @@
 }  // namespace base
 }  // namespace perfetto
 
-#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAT_SET_H_
+#endif  // INCLUDE_PERFETTO_BASE_FLAT_SET_H_
diff --git a/include/perfetto/base/logging.h b/include/perfetto/base/logging.h
index 2691438..aeebf0b 100644
--- a/include/perfetto/base/logging.h
+++ b/include/perfetto/base/logging.h
@@ -24,6 +24,9 @@
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/export.h"
 
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
 // TODO(primiano): move this to base/build_config.h, turn into
 // PERFETTO_BUILDFLAG(DCHECK_IS_ON) and update call sites to use that instead.
 #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
diff --git a/include/perfetto/ext/base/proc_utils.h b/include/perfetto/base/proc_utils.h
similarity index 90%
rename from include/perfetto/ext/base/proc_utils.h
rename to include/perfetto/base/proc_utils.h
index d5bacb9..8818ec0 100644
--- a/include/perfetto/ext/base/proc_utils.h
+++ b/include/perfetto/base/proc_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef INCLUDE_PERFETTO_EXT_BASE_PROC_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_PROC_UTILS_H_
+#ifndef INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
+#define INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
 
 #include <stdint.h>
 
@@ -54,4 +54,4 @@
 }  // namespace base
 }  // namespace perfetto
 
-#endif  // INCLUDE_PERFETTO_EXT_BASE_PROC_UTILS_H_
+#endif  // INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
diff --git a/include/perfetto/ext/base/thread_utils.h b/include/perfetto/base/thread_utils.h
similarity index 66%
rename from include/perfetto/ext/base/thread_utils.h
rename to include/perfetto/base/thread_utils.h
index 4ad1825..89da007 100644
--- a/include/perfetto/ext/base/thread_utils.h
+++ b/include/perfetto/base/thread_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#ifndef INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
+#define INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
 
 #include <stdint.h>
 
@@ -38,35 +38,40 @@
 namespace base {
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-using PlatformThreadID = pid_t;
-inline PlatformThreadID GetThreadId() {
+using PlatformThreadId = pid_t;
+inline PlatformThreadId GetThreadId() {
   return gettid();
 }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX)
-using PlatformThreadID = pid_t;
-inline PlatformThreadID GetThreadId() {
+using PlatformThreadId = pid_t;
+inline PlatformThreadId GetThreadId() {
   return static_cast<pid_t>(syscall(__NR_gettid));
 }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-using PlatformThreadID = zx_handle_t;
-inline PlatformThreadID GetThreadId() {
-  return static_cast<pid_t>(zx_thread_self());
+using PlatformThreadId = zx_handle_t;
+inline PlatformThreadId GetThreadId() {
+  return zx_thread_self();
 }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
-using PlatformThreadID = uint64_t;
-inline PlatformThreadID GetThreadId() {
+using PlatformThreadId = uint64_t;
+inline PlatformThreadId GetThreadId() {
   uint64_t tid;
   pthread_threadid_np(nullptr, &tid);
   return tid;
 }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-using PlatformThreadID = uint64_t;
-inline PlatformThreadID GetThreadId() {
+using PlatformThreadId = uint64_t;
+inline PlatformThreadId GetThreadId() {
   return static_cast<uint64_t>(GetCurrentThreadId());
 }
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+using PlatformThreadId = pid_t;
+inline PlatformThreadId GetThreadId() {
+  return reinterpret_cast<int32_t>(pthread_self());
+}
 #else  // Default to pthreads in case no OS is set.
-using PlatformThreadID = pthread_t;
-inline PlatformThreadID GetThreadId() {
+using PlatformThreadId = pthread_t;
+inline PlatformThreadId GetThreadId() {
   return pthread_self();
 }
 #endif
@@ -74,4 +79,4 @@
 }  // namespace base
 }  // namespace perfetto
 
-#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#endif  // INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
index 8c81faa..0ee6042 100644
--- a/include/perfetto/base/time.h
+++ b/include/perfetto/base/time.h
@@ -110,7 +110,25 @@
   return GetWallTimeNs();
 }
 
-#else
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+
+// Tracing time doesn't need to work on NaCl since its going away shortly. We
+// just need to compile on it. The only function NaCl could support is
+// GetWallTimeNs(), but to prevent false hope we leave it unimplemented.
+
+inline TimeNanos GetWallTimeNs() {
+  return TimeNanos(0);
+}
+
+inline TimeNanos GetThreadCPUTimeNs() {
+  return TimeNanos(0);
+}
+
+inline TimeNanos GetBootTimeNs() {
+  return TimeNanos(0);
+}
+
+#else  // posix
 
 constexpr clockid_t kWallTimeClockSource = CLOCK_MONOTONIC;
 
@@ -139,7 +157,6 @@
 inline TimeNanos GetThreadCPUTimeNs() {
   return GetTimeInternalNs(CLOCK_THREAD_CPUTIME_ID);
 }
-
 #endif
 
 inline TimeSeconds GetBootTimeS() {
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 7d76c84..052d277 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -20,7 +20,6 @@
     "container_annotations.h",
     "event_fd.h",
     "file_utils.h",
-    "flat_set.h",
     "hash.h",
     "lookup_set.h",
     "metatrace.h",
@@ -29,7 +28,6 @@
     "optional.h",
     "paged_memory.h",
     "pipe.h",
-    "proc_utils.h",
     "scoped_file.h",
     "small_set.h",
     "string_splitter.h",
@@ -40,7 +38,6 @@
     "thread_annotations.h",
     "thread_checker.h",
     "thread_task_runner.h",
-    "thread_utils.h",
     "unix_task_runner.h",
     "utils.h",
     "uuid.h",
@@ -54,7 +51,5 @@
     sources += [ "unix_socket.h" ]
   }
   public_configs = [ "../../../../gn:asan_instrumentation" ]
-  public_deps = [
-    "../../base",
-  ]
+  public_deps = [ "../../base" ]
 }
diff --git a/include/perfetto/ext/base/metatrace.h b/include/perfetto/ext/base/metatrace.h
index 2c587c3..f5eb057 100644
--- a/include/perfetto/ext/base/metatrace.h
+++ b/include/perfetto/ext/base/metatrace.h
@@ -23,10 +23,10 @@
 #include <string>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/metatrace_events.h"
 #include "perfetto/ext/base/thread_annotations.h"
-#include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/ext/base/utils.h"
 
 // A facility to trace execution of the perfetto codebase itself.
diff --git a/include/perfetto/ext/base/metatrace_events.h b/include/perfetto/ext/base/metatrace_events.h
index 4f62244..e3cfb77 100644
--- a/include/perfetto/ext/base/metatrace_events.h
+++ b/include/perfetto/ext/base/metatrace_events.h
@@ -59,7 +59,8 @@
   F(TRACE_WRITER_COMMIT_STARTUP_WRITER_BATCH), \
   F(FTRACE_READ_TICK), \
   F(FTRACE_CPU_READ_CYCLE), \
-  F(FTRACE_CPU_READ_BATCH)
+  F(FTRACE_CPU_READ_BATCH), \
+  F(KALLSYMS_PARSE)
 
 // Append only, see above.
 //
diff --git a/include/perfetto/ext/base/string_writer.h b/include/perfetto/ext/base/string_writer.h
index 681324f..428b17e 100644
--- a/include/perfetto/ext/base/string_writer.h
+++ b/include/perfetto/ext/base/string_writer.h
@@ -19,8 +19,9 @@
 
 #include <inttypes.h>
 #include <math.h>
-#include <stdlib.h>
 #include <string.h>
+#include <cmath>
+#include <cstdlib>
 #include <limits>
 
 #include "perfetto/base/logging.h"
@@ -69,7 +70,7 @@
   // digits of the integer is less than |padding|.
   template <char padchar, uint64_t padding>
   void AppendPaddedInt(int64_t sign_value) {
-    const bool negate = signbit(static_cast<double>(sign_value));
+    const bool negate = std::signbit(static_cast<double>(sign_value));
     uint64_t absolute_value = static_cast<uint64_t>(std::abs(sign_value));
     AppendPaddedInt<padchar, padding>(absolute_value, negate);
   }
diff --git a/include/perfetto/ext/base/thread_task_runner.h b/include/perfetto/ext/base/thread_task_runner.h
index fac4553..6579fa5 100644
--- a/include/perfetto/ext/base/thread_task_runner.h
+++ b/include/perfetto/ext/base/thread_task_runner.h
@@ -34,7 +34,9 @@
 //
 class ThreadTaskRunner {
  public:
-  static ThreadTaskRunner CreateAndStart() { return ThreadTaskRunner(); }
+  static ThreadTaskRunner CreateAndStart(const std::string& name = "") {
+    return ThreadTaskRunner(name);
+  }
 
   ThreadTaskRunner(const ThreadTaskRunner&) = delete;
   ThreadTaskRunner& operator=(const ThreadTaskRunner&) = delete;
@@ -43,6 +45,14 @@
   ThreadTaskRunner& operator=(ThreadTaskRunner&&);
   ~ThreadTaskRunner();
 
+  // Executes the given function on the task runner thread and blocks the caller
+  // thread until the function has run.
+  void PostTaskAndWaitForTesting(std::function<void()>);
+
+  // Can be called from another thread to get the CPU time of the thread the
+  // task-runner is executing on.
+  uint64_t GetThreadCPUTimeNsForTesting();
+
   // Returns a pointer to the UnixTaskRunner, which is valid for the lifetime of
   // this ThreadTaskRunner object (unless this object is moved-from, in which
   // case the pointer remains valid for the lifetime of the new owning
@@ -53,10 +63,11 @@
   UnixTaskRunner* get() const { return task_runner_; }
 
  private:
-  ThreadTaskRunner();
+  explicit ThreadTaskRunner(const std::string& name);
   void RunTaskThread(std::function<void(UnixTaskRunner*)> initializer);
 
   std::thread thread_;
+  std::string name_;
   UnixTaskRunner* task_runner_ = nullptr;
 };
 
diff --git a/include/perfetto/ext/base/unix_socket.h b/include/perfetto/ext/base/unix_socket.h
index a0dac27..07e0f58 100644
--- a/include/perfetto/ext/base/unix_socket.h
+++ b/include/perfetto/ext/base/unix_socket.h
@@ -48,9 +48,7 @@
 class UnixSocketRaw {
  public:
   // Creates a new unconnected unix socket.
-  static UnixSocketRaw CreateMayFail(SockFamily family, SockType type) {
-    return UnixSocketRaw(family, type);
-  }
+  static UnixSocketRaw CreateMayFail(SockFamily family, SockType type);
 
   // Crates a pair of connected sockets.
   static std::pair<UnixSocketRaw, UnixSocketRaw> CreatePair(SockFamily,
diff --git a/include/perfetto/ext/base/unix_task_runner.h b/include/perfetto/ext/base/unix_task_runner.h
index 9ced3b9..386c01f 100644
--- a/include/perfetto/ext/base/unix_task_runner.h
+++ b/include/perfetto/ext/base/unix_task_runner.h
@@ -19,11 +19,11 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/task_runner.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/event_fd.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/thread_checker.h"
-#include "perfetto/ext/base/thread_utils.h"
 
 #include <poll.h>
 #include <chrono>
@@ -87,7 +87,7 @@
   void RunFileDescriptorWatch(int fd);
 
   ThreadChecker thread_checker_;
-  PlatformThreadID created_thread_id_ = GetThreadId();
+  PlatformThreadId created_thread_id_ = GetThreadId();
 
   // On Linux, an eventfd(2) used to waking up the task runner when a new task
   // is posted. Otherwise the read end of a pipe used for the same purpose.
diff --git a/include/perfetto/ext/base/utils.h b/include/perfetto/ext/base/utils.h
index f7bfdcd..aadded6 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -28,13 +28,13 @@
 #endif
 
 #define PERFETTO_EINTR(x)                                   \
-  ({                                                        \
+  ([&] {                                                    \
     decltype(x) eintr_wrapper_result;                       \
     do {                                                    \
       eintr_wrapper_result = (x);                           \
     } while (eintr_wrapper_result == -1 && errno == EINTR); \
-    eintr_wrapper_result;                                   \
-  })
+    return eintr_wrapper_result;                            \
+  }())
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 // TODO(brucedawson) - create a ::perfetto::base::IOSize to replace this.
diff --git a/include/perfetto/ext/base/uuid.h b/include/perfetto/ext/base/uuid.h
index 8d13751..1b4c538 100644
--- a/include/perfetto/ext/base/uuid.h
+++ b/include/perfetto/ext/base/uuid.h
@@ -54,13 +54,13 @@
     set_lsb(lsb);
     set_msb(msb);
   }
+  void set_msb(int64_t msb) { memcpy(data_.data() + 8, &msb, 8); }
+  void set_lsb(int64_t lsb) { memcpy(data_.data(), &lsb, 8); }
 
   std::string ToString() const;
   std::string ToPrettyString() const;
 
  private:
-  void set_msb(int64_t msb) { memcpy(data_.data() + 8, &msb, 8); }
-  void set_lsb(int64_t lsb) { memcpy(data_.data(), &lsb, 8); }
   std::array<uint8_t, 16> data_{};
 };
 
diff --git a/include/perfetto/ext/ipc/service_proxy.h b/include/perfetto/ext/ipc/service_proxy.h
index 98f111c..02c2dc2 100644
--- a/include/perfetto/ext/ipc/service_proxy.h
+++ b/include/perfetto/ext/ipc/service_proxy.h
@@ -26,6 +26,7 @@
 #include <memory>
 #include <string>
 
+#include "perfetto/base/export.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/ipc/deferred.h"
 
@@ -38,7 +39,7 @@
 // The base class for the client-side autogenerated stubs that forward method
 // invocations to the host. All the methods of this class are meant to be called
 // only by the autogenerated code.
-class ServiceProxy {
+class PERFETTO_EXPORT ServiceProxy {
  public:
   class EventListener {
    public:
diff --git a/include/perfetto/ext/traced/BUILD.gn b/include/perfetto/ext/traced/BUILD.gn
index 1f58ac2..631202c 100644
--- a/include/perfetto/ext/traced/BUILD.gn
+++ b/include/perfetto/ext/traced/BUILD.gn
@@ -25,7 +25,5 @@
     "../../../../protos/perfetto/common:zero",
     "../base",
   ]
-  sources = [
-    "sys_stats_counters.h",
-  ]
+  sources = [ "sys_stats_counters.h" ]
 }
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index c097a6e..695d78e 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -106,11 +106,12 @@
       BufferExhaustedPolicy buffer_exhausted_policy =
           BufferExhaustedPolicy::kDefault) = 0;
 
-  // If TracingService::ConnectProducer is called with |in_process=true|,
-  // this returns the producer's SharedMemoryArbiter which can be used
-  // to create TraceWriters which is able to directly commit chunks
-  // without going through an IPC layer.
-  virtual SharedMemoryArbiter* GetInProcessShmemArbiter() = 0;
+  // In some cases you can access the producer's SharedMemoryArbiter (for
+  // example if TracingService::ConnectProducer is called with
+  // |in_process=true|). The SharedMemoryArbiter can be used to create
+  // TraceWriters which is able to directly commit chunks. For the
+  // |in_process=true| case this can be done without going through an IPC layer.
+  virtual SharedMemoryArbiter* MaybeSharedMemoryArbiter() = 0;
 
   // Called in response to a Producer::Flush(request_id) call after all data
   // for the flush request has been committed.
diff --git a/include/perfetto/ext/tracing/ipc/consumer_ipc_client.h b/include/perfetto/ext/tracing/ipc/consumer_ipc_client.h
index b39d17c..bb88073 100644
--- a/include/perfetto/ext/tracing/ipc/consumer_ipc_client.h
+++ b/include/perfetto/ext/tracing/ipc/consumer_ipc_client.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <string>
 
+#include "perfetto/base/export.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
 
 namespace perfetto {
@@ -31,7 +32,7 @@
 //   Consumer(s) of the tracing library.
 // Implemented in:
 //   src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
-class ConsumerIPCClient {
+class PERFETTO_EXPORT ConsumerIPCClient {
  public:
   // Connects to the producer port of the Service listening on the given
   // |service_sock_name|. If the connection is successful, the OnConnect()
diff --git a/include/perfetto/ext/tracing/ipc/default_socket.h b/include/perfetto/ext/tracing/ipc/default_socket.h
index 85a1de0..6f1fafb 100644
--- a/include/perfetto/ext/tracing/ipc/default_socket.h
+++ b/include/perfetto/ext/tracing/ipc/default_socket.h
@@ -17,10 +17,12 @@
 #ifndef INCLUDE_PERFETTO_EXT_TRACING_IPC_DEFAULT_SOCKET_H_
 #define INCLUDE_PERFETTO_EXT_TRACING_IPC_DEFAULT_SOCKET_H_
 
+#include "perfetto/base/export.h"
+
 namespace perfetto {
 
-const char* GetConsumerSocket();
-const char* GetProducerSocket();
+PERFETTO_EXPORT const char* GetConsumerSocket();
+PERFETTO_EXPORT const char* GetProducerSocket();
 
 }  // namespace perfetto
 
diff --git a/include/perfetto/ext/tracing/ipc/producer_ipc_client.h b/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
index e6092e8..6c1bdba 100644
--- a/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
+++ b/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <string>
 
+#include "perfetto/base/export.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
 
 namespace perfetto {
@@ -31,7 +32,7 @@
 //   Producer(s) of the tracing library.
 // Implemented in:
 //   src/tracing/ipc/producer/producer_ipc_client_impl.cc
-class ProducerIPCClient {
+class PERFETTO_EXPORT ProducerIPCClient {
  public:
   // Connects to the producer port of the Service listening on the given
   // |service_sock_name|. If the connection is successful, the OnConnect()
diff --git a/include/perfetto/ext/tracing/ipc/service_ipc_host.h b/include/perfetto/ext/tracing/ipc/service_ipc_host.h
index 86915af..ed3deb9 100644
--- a/include/perfetto/ext/tracing/ipc/service_ipc_host.h
+++ b/include/perfetto/ext/tracing/ipc/service_ipc_host.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 
+#include "perfetto/base/export.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 
@@ -34,7 +35,7 @@
 //   The code in the tracing client that will host the service e.g., traced.
 // Implemented in:
 //   src/tracing/ipc/service/service_ipc_host_impl.cc
-class ServiceIPCHost {
+class PERFETTO_EXPORT ServiceIPCHost {
  public:
   static std::unique_ptr<ServiceIPCHost> CreateInstance(base::TaskRunner*);
   virtual ~ServiceIPCHost();
diff --git a/include/perfetto/profiling/BUILD.gn b/include/perfetto/profiling/BUILD.gn
index ce7c188..17dc5db 100644
--- a/include/perfetto/profiling/BUILD.gn
+++ b/include/perfetto/profiling/BUILD.gn
@@ -13,19 +13,13 @@
 # limitations under the License.
 
 source_set("pprof_builder") {
-  sources = [
-    "pprof_builder.h",
-  ]
+  sources = [ "pprof_builder.h" ]
 }
 
 source_set("normalize") {
-  sources = [
-    "normalize.h",
-  ]
+  sources = [ "normalize.h" ]
 }
 
 source_set("deobfuscator") {
-  sources = [
-    "deobfuscator.h",
-  ]
+  sources = [ "deobfuscator.h" ]
 }
diff --git a/include/perfetto/protozero/BUILD.gn b/include/perfetto/protozero/BUILD.gn
index b528a5b..ee0f77a 100644
--- a/include/perfetto/protozero/BUILD.gn
+++ b/include/perfetto/protozero/BUILD.gn
@@ -13,9 +13,7 @@
 # limitations under the License.
 
 source_set("protozero") {
-  public_deps = [
-    "../base",
-  ]
+  public_deps = [ "../base" ]
   sources = [
     "contiguous_memory_range.h",
     "copyable_ptr.h",
diff --git a/include/perfetto/public/BUILD.gn b/include/perfetto/public/BUILD.gn
index 0ec88f1..13eb6c1 100644
--- a/include/perfetto/public/BUILD.gn
+++ b/include/perfetto/public/BUILD.gn
@@ -13,7 +13,5 @@
 # limitations under the License.
 
 source_set("public") {
-  sources = [
-    "consumer_api.h",
-  ]
+  sources = [ "consumer_api.h" ]
 }
diff --git a/include/perfetto/trace_processor/BUILD.gn b/include/perfetto/trace_processor/BUILD.gn
index 1c419f9..8db2a2f 100644
--- a/include/perfetto/trace_processor/BUILD.gn
+++ b/include/perfetto/trace_processor/BUILD.gn
@@ -24,12 +24,8 @@
 }
 
 source_set("storage") {
-  sources = [
-    "trace_processor_storage.h",
-  ]
-  public_deps = [
-    ":basic_types",
-  ]
+  sources = [ "trace_processor_storage.h" ]
+  public_deps = [ ":basic_types" ]
 }
 
 source_set("basic_types") {
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index db67e79..b584126 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -40,9 +40,15 @@
   // sort ignoring any internal heureustics to skip sorting parts of the data.
   bool force_full_sort = false;
 
-  // When set to a non-zero value, this overrides the default block size used
-  // by the StringPool. For defaults, see kDefaultBlockSize in string_pool.h.
-  size_t string_pool_block_size_bytes = 0;
+  // When set to false, this option makes the trace processor not include ftrace
+  // events in the raw table; this makes converting events back to the systrace
+  // text format impossible. On the other hand, it also saves ~50% of memory
+  // usage of trace processor. For reference, Studio intends to use this option.
+  //
+  // Note: "generic" ftrace events will be parsed into the raw table even if
+  // this flag is false and all other events which parse into the raw table are
+  // unaffected by this flag.
+  bool ingest_ftrace_in_raw_table = true;
 };
 
 // Represents a dynamically typed value returned by SQL.
diff --git a/include/perfetto/tracing.h b/include/perfetto/tracing.h
index be9ef6d..fca7c70 100644
--- a/include/perfetto/tracing.h
+++ b/include/perfetto/tracing.h
@@ -33,5 +33,6 @@
 #include "perfetto/tracing/tracing_backend.h"
 #include "perfetto/tracing/track_event.h"
 #include "perfetto/tracing/track_event_interned_data_index.h"
+#include "perfetto/tracing/track_event_legacy.h"
 
 #endif  // INCLUDE_PERFETTO_TRACING_H_
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 01294bd..e6c68c4 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -14,7 +14,6 @@
 
 source_set("tracing") {
   public_deps = [
-    "../../../gn:default_deps",
     "../../../protos/perfetto/common:cpp",
     "../../../protos/perfetto/trace:zero",
     "../../../protos/perfetto/trace/interned_data:zero",
@@ -41,8 +40,10 @@
     "trace_writer_base.h",
     "tracing.h",
     "tracing_backend.h",
+    "track.h",
     "track_event.h",
     "track_event_category_registry.h",
     "track_event_interned_data_index.h",
+    "track_event_legacy.h",
   ]
 }
diff --git a/include/perfetto/tracing/core/BUILD.gn b/include/perfetto/tracing/core/BUILD.gn
index a7f4a4c..a359f63 100644
--- a/include/perfetto/tracing/core/BUILD.gn
+++ b/include/perfetto/tracing/core/BUILD.gn
@@ -31,7 +31,5 @@
 # forward_decls.h without polluting their public_deps with the proto-generated
 # {common,config}:cpp, which would slow-down build time.
 source_set("forward_decls") {
-  sources = [
-    "forward_decls.h",
-  ]
+  sources = [ "forward_decls.h" ]
 }
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index 923e176..1a2f57e 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -369,8 +369,6 @@
   // Setup/Start/Stop notifications and makes the Trace() method work when
   // tracing is enabled and the data source is selected.
   // This must be called after Tracing::Initialize().
-  // The caller must also use the DEFINE_DATA_SOURCE_STATIC_MEMBERS() macro
-  // documented below.
   // Can return false to signal failure if attemping to register more than
   // kMaxDataSources (32) data sources types.
   static bool Register(const DataSourceDescriptor& descriptor) {
@@ -455,19 +453,20 @@
 
 }  // namespace perfetto
 
+// If placed at the end of a macro declaration, eats the semicolon at the end of
+// the macro invocation (e.g., "MACRO(...);") to avoid warnings about extra
+// semicolons.
+#define PERFETTO_INTERNAL_SWALLOW_SEMICOLON() \
+  extern int perfetto_internal_unused
+
 // Not needed -- only here for backwards compatibility.
 // TODO(skyostil): Remove this macro.
-#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...)
+#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...) \
+  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
 
-// The API client must use this in a translation unit. This is because it needs
-// to instantiate the static storage for the datasource to allow the fastpath
-// enabled check.
-#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...)        \
-  template <>                                                  \
-  perfetto::internal::DataSourceStaticState                    \
-      perfetto::DataSource<__VA_ARGS__>::static_state_{};      \
-  template <>                                                  \
-  thread_local perfetto::internal::DataSourceThreadLocalState* \
-      perfetto::DataSource<__VA_ARGS__>::tls_state_ = nullptr
+// Not needed -- only here for backwards compatibility.
+// TODO(skyostil): Remove this macro.
+#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...) \
+  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
 
 #endif  // INCLUDE_PERFETTO_TRACING_DATA_SOURCE_H_
diff --git a/include/perfetto/tracing/debug_annotation.h b/include/perfetto/tracing/debug_annotation.h
index 996861c..f13fbd9 100644
--- a/include/perfetto/tracing/debug_annotation.h
+++ b/include/perfetto/tracing/debug_annotation.h
@@ -21,6 +21,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 namespace perfetto {
@@ -56,6 +57,12 @@
 void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
                           const DebugAnnotation&);
 
+template <typename T>
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+                          const std::unique_ptr<T>& value) {
+  WriteDebugAnnotation(annotation, *value);
+}
+
 }  // namespace internal
 }  // namespace perfetto
 
diff --git a/include/perfetto/tracing/event_context.h b/include/perfetto/tracing/event_context.h
index da1607b..fe9c4bb 100644
--- a/include/perfetto/tracing/event_context.h
+++ b/include/perfetto/tracing/event_context.h
@@ -35,9 +35,15 @@
 //                       dbg->set_int_value(1234);
 //                     });
 //
-class EventContext {
+class PERFETTO_EXPORT EventContext {
  public:
   EventContext(EventContext&&) = default;
+
+  // For Chromium during the transition phase to the client library.
+  // TODO(eseckler): Remove once Chromium has switched to client lib entirely.
+  explicit EventContext(protos::pbzero::TrackEvent* event)
+      : event_(event), incremental_state_(nullptr) {}
+
   ~EventContext();
 
   protos::pbzero::TrackEvent* event() const { return event_; }
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 1debee3..e835155 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -22,6 +22,7 @@
 #include "perfetto/tracing/data_source.h"
 #include "perfetto/tracing/event_context.h"
 #include "perfetto/tracing/internal/track_event_internal.h"
+#include "perfetto/tracing/track.h"
 #include "perfetto/tracing/track_event_category_registry.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
@@ -30,6 +31,34 @@
 
 namespace perfetto {
 namespace internal {
+namespace {
+
+// A template helper for determining whether a type can be used as a track event
+// lambda, i.e., it has the signature "void(EventContext)". This is achieved by
+// checking that we can pass an EventContext value (the inner declval) into a T
+// instance (the outer declval). If this is a valid expression, the result
+// evaluates to sizeof(0), i.e., true.
+// TODO(skyostil): Replace this with std::is_convertible<std::function<...>>
+// once we have C++14.
+template <typename T>
+static constexpr bool IsValidTraceLambdaImpl(
+    typename std::enable_if<static_cast<bool>(
+        sizeof(std::declval<T>()(std::declval<EventContext>()), 0))>::type* =
+        nullptr) {
+  return true;
+}
+
+template <typename T>
+static constexpr bool IsValidTraceLambdaImpl(...) {
+  return false;
+}
+
+template <typename T>
+static constexpr bool IsValidTraceLambda() {
+  return IsValidTraceLambdaImpl<T>(nullptr);
+}
+
+}  // namespace
 
 struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits {
   using IncrementalStateType = TrackEventIncrementalState;
@@ -87,24 +116,73 @@
   }
 
   // Once we've determined tracing to be enabled for this category, actually
-  // write a trace event. Outlined to avoid bloating code at the actual trace
-  // point.
-  // TODO(skyostil): Investigate whether this should be fully outlined to reduce
-  // binary size.
+  // write a trace event onto this thread's default track. Outlined to avoid
+  // bloating code (mostly stack depth) at the actual trace point.
+  //
+  // To minimize call overhead at each trace point, we provide the following
+  // trace point argument variants:
+  //
+  // - None
+  // - Lambda
+  // - One debug annotation
+  // - Two debug annotations
+  // - Track
+  // - Track + Lambda
+  // - Track + one debug annotation
+  // - Track + two debug annotations
+
+  // Trace point which takes no arguments.
+  template <size_t CategoryIndex>
+  static void TraceForCategory(uint32_t instances,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, event_name, type);
+  }
+
+  // Trace point which takes a lambda function argument.
   template <size_t CategoryIndex,
-            typename ArgumentFunction = void (*)(EventContext)>
-  static void TraceForCategory(
-      uint32_t instances,
-      const char* event_name,
-      perfetto::protos::pbzero::TrackEvent::Type type,
-      ArgumentFunction arg_function = [](EventContext) {}) PERFETTO_NO_INLINE {
-    Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
-        instances, [&](typename Base::TraceContext ctx) {
-          // TODO(skyostil): Intern categories at compile time.
-          arg_function(TrackEventInternal::WriteEvent(
-              ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
-              Registry->GetCategory(CategoryIndex)->name, event_name, type));
-        });
+            typename ArgumentFunction = void (*)(EventContext),
+            typename ArgumentFunctionCheck = typename std::enable_if<
+                IsValidTraceLambda<ArgumentFunction>()>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               ArgumentFunction arg_function)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, event_name, type, Track(),
+                                        std::move(arg_function));
+  }
+
+  // This variant of the inner trace point takes a Track argument which can be
+  // used to emit events on a non-default track.
+  template <size_t CategoryIndex,
+            typename TrackType,
+            typename TrackTypeCheck = typename std::enable_if<
+                std::is_convertible<TrackType, Track>::value>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track) PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, event_name, type, track);
+  }
+
+  // Trace point with a track and a lambda function.
+  template <size_t CategoryIndex,
+            typename TrackType,
+            typename ArgumentFunction = void (*)(EventContext),
+            typename ArgumentFunctionCheck = typename std::enable_if<
+                IsValidTraceLambda<ArgumentFunction>()>::type,
+            typename TrackTypeCheck = typename std::enable_if<
+                std::is_convertible<TrackType, Track>::value>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track,
+                               ArgumentFunction arg_function)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, event_name, type, track,
+                                        std::move(arg_function));
   }
 
   // Trace point with one debug annotation.
@@ -124,27 +202,51 @@
                                perfetto::protos::pbzero::TrackEvent::Type type,
                                const char* arg_name,
                                ArgType&& arg_value) PERFETTO_ALWAYS_INLINE {
-    TraceForCategoryWithDebugAnnotations<CategoryIndex, ArgType>(
-        instances, event_name, type, arg_name,
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, Track, ArgType>(
+        instances, event_name, type, Track(), arg_name,
         std::forward<ArgType>(arg_value));
   }
 
-  template <size_t CategoryIndex, typename ArgType>
+  // A one argument trace point which takes an explicit track.
+  template <size_t CategoryIndex, typename TrackType, typename ArgType>
+  static void TraceForCategory(uint32_t instances,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track,
+                               const char* arg_name,
+                               ArgType&& arg_value) PERFETTO_ALWAYS_INLINE {
+    PERFETTO_DCHECK(track);
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, TrackType, ArgType>(
+        instances, event_name, type, track, arg_name,
+        std::forward<ArgType>(arg_value));
+  }
+
+  template <size_t CategoryIndex, typename TrackType, typename ArgType>
   static void TraceForCategoryWithDebugAnnotations(
       uint32_t instances,
       const char* event_name,
       perfetto::protos::pbzero::TrackEvent::Type type,
+      const TrackType& track,
       const char* arg_name,
       typename internal::DebugAnnotationArg<ArgType>::type arg_value)
       PERFETTO_NO_INLINE {
     Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
         instances, [&](typename Base::TraceContext ctx) {
-          // TODO(skyostil): Intern categories at compile time.
-          auto event_ctx = TrackEventInternal::WriteEvent(
-              ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
-              Registry->GetCategory(CategoryIndex)->name, event_name, type);
-          TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
-                                                 arg_value);
+          {
+            // TODO(skyostil): Intern categories at compile time.
+            auto event_ctx = TrackEventInternal::WriteEvent(
+                ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+                Registry->GetCategory(CategoryIndex)->name, event_name, type);
+            if (track)
+              event_ctx.event()->set_track_uuid(track.uuid);
+            TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+                                                   arg_value);
+          }
+          if (track) {
+            TrackEventInternal::WriteTrackDescriptorIfNeeded(
+                track, ctx.tls_inst_->trace_writer.get(),
+                ctx.GetIncrementalState());
+          }
         });
   }
 
@@ -160,16 +262,43 @@
                                ArgType&& arg_value,
                                const char* arg_name2,
                                ArgType2&& arg_value2) PERFETTO_ALWAYS_INLINE {
-    TraceForCategoryWithDebugAnnotations<CategoryIndex, ArgType, ArgType2>(
-        instances, event_name, type, arg_name, std::forward<ArgType>(arg_value),
-        arg_name2, std::forward<ArgType2>(arg_value2));
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, Track, ArgType,
+                                         ArgType2>(
+        instances, event_name, type, Track(), arg_name,
+        std::forward<ArgType>(arg_value), arg_name2,
+        std::forward<ArgType2>(arg_value2));
   }
 
-  template <size_t CategoryIndex, typename ArgType, typename ArgType2>
+  // A two argument trace point which takes an explicit track.
+  template <size_t CategoryIndex,
+            typename TrackType,
+            typename ArgType,
+            typename ArgType2>
+  static void TraceForCategory(uint32_t instances,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track,
+                               const char* arg_name,
+                               ArgType&& arg_value,
+                               const char* arg_name2,
+                               ArgType2&& arg_value2) PERFETTO_ALWAYS_INLINE {
+    PERFETTO_DCHECK(track);
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, TrackType, ArgType,
+                                         ArgType2>(
+        instances, event_name, type, track, arg_name,
+        std::forward<ArgType>(arg_value), arg_name2,
+        std::forward<ArgType2>(arg_value2));
+  }
+
+  template <size_t CategoryIndex,
+            typename TrackType,
+            typename ArgType,
+            typename ArgType2>
   static void TraceForCategoryWithDebugAnnotations(
       uint32_t instances,
       const char* event_name,
       perfetto::protos::pbzero::TrackEvent::Type type,
+      TrackType track,
       const char* arg_name,
       typename internal::DebugAnnotationArg<ArgType>::type arg_value,
       const char* arg_name2,
@@ -178,16 +307,27 @@
     Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
         instances, [&](typename Base::TraceContext ctx) {
           // TODO(skyostil): Intern categories at compile time.
-          auto event_ctx = TrackEventInternal::WriteEvent(
-              ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
-              Registry->GetCategory(CategoryIndex)->name, event_name, type);
-          TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
-                                                 arg_value);
-          TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name2,
-                                                 arg_value2);
+          {
+            auto event_ctx = TrackEventInternal::WriteEvent(
+                ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+                Registry->GetCategory(CategoryIndex)->name, event_name, type);
+            if (track)
+              event_ctx.event()->set_track_uuid(track.uuid);
+            TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+                                                   arg_value);
+            TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name2,
+                                                   arg_value2);
+          }
+          if (track) {
+            TrackEventInternal::WriteTrackDescriptorIfNeeded(
+                track, ctx.tls_inst_->trace_writer.get(),
+                ctx.GetIncrementalState());
+          }
         });
   }
 
+  // Initialize the track event library. Should be called before tracing is
+  // enabled.
   static bool Register() {
     // Registration is performed out-of-line so users don't need to depend on
     // DataSourceDescriptor C++ bindings.
@@ -195,6 +335,29 @@
         [](const DataSourceDescriptor& dsd) { return Base::Register(dsd); });
   }
 
+  // Record metadata about different types of timeline tracks. See Track.
+  static void SetTrackDescriptor(
+      const Track& track,
+      std::function<void(protos::pbzero::TrackDescriptor*)> callback) {
+    SetTrackDescriptorImpl(track, std::move(callback));
+  }
+
+  static void SetProcessDescriptor(
+      std::function<void(protos::pbzero::TrackDescriptor*)> callback,
+      const ProcessTrack& track = ProcessTrack::Current()) {
+    SetTrackDescriptorImpl(std::move(track), std::move(callback));
+  }
+
+  static void SetThreadDescriptor(
+      std::function<void(protos::pbzero::TrackDescriptor*)> callback,
+      const ThreadTrack& track = ThreadTrack::Current()) {
+    SetTrackDescriptorImpl(std::move(track), std::move(callback));
+  }
+
+  static void EraseTrackDescriptor(const Track& track) {
+    TrackRegistry::Get()->EraseTrack(track);
+  }
+
  private:
   // Each category has its own enabled/disabled state, stored in the category
   // registry.
@@ -204,6 +367,55 @@
       return Registry->GetCategoryState(CategoryIndex);
     }
   };
+
+  // TODO(skyostil): Make |CategoryIndex| a regular parameter to reuse trace
+  // point code across different categories.
+  template <size_t CategoryIndex,
+            typename TrackType = Track,
+            typename ArgumentFunction = void (*)(EventContext),
+            typename ArgumentFunctionCheck = typename std::enable_if<
+                IsValidTraceLambda<ArgumentFunction>()>::type,
+            typename TrackTypeCheck = typename std::enable_if<
+                std::is_convertible<TrackType, Track>::value>::type>
+  static void TraceForCategoryImpl(
+      uint32_t instances,
+      const char* event_name,
+      perfetto::protos::pbzero::TrackEvent::Type type,
+      const TrackType& track = Track(),
+      ArgumentFunction arg_function = [](EventContext) {
+      }) PERFETTO_ALWAYS_INLINE {
+    Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
+        instances, [&](typename Base::TraceContext ctx) {
+          {
+            // TODO(skyostil): Intern categories at compile time.
+            auto event_ctx = TrackEventInternal::WriteEvent(
+                ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+                Registry->GetCategory(CategoryIndex)->name, event_name, type);
+            if (track)
+              event_ctx.event()->set_track_uuid(track.uuid);
+            arg_function(std::move(event_ctx));
+          }
+          if (track) {
+            TrackEventInternal::WriteTrackDescriptorIfNeeded(
+                track, ctx.tls_inst_->trace_writer.get(),
+                ctx.GetIncrementalState());
+          }
+        });
+  }
+
+  // Records a track descriptor into the track descriptor registry and, if we
+  // are tracing, also mirrors the descriptor into the trace.
+  template <typename TrackType>
+  static void SetTrackDescriptorImpl(
+      const TrackType& track,
+      std::function<void(protos::pbzero::TrackDescriptor*)> callback) {
+    TrackRegistry::Get()->UpdateTrack(
+        track, [&](protos::pbzero::TrackDescriptor* desc) { callback(desc); });
+    Base::template Trace([&](typename Base::TraceContext ctx) {
+      TrackEventInternal::WriteTrackDescriptor(
+          track, ctx.tls_inst_->trace_writer.get());
+    });
+  }
 };
 
 }  // namespace internal
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index e37a30b..a4ee050 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -17,15 +17,17 @@
 #ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
 #define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
 
-#include <unordered_map>
-
+#include "perfetto/base/flat_set.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/tracing/core/forward_decls.h"
 #include "perfetto/tracing/debug_annotation.h"
 #include "perfetto/tracing/trace_writer_base.h"
+#include "perfetto/tracing/track.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
+#include <set>
+
 namespace perfetto {
 class EventContext;
 namespace protos {
@@ -70,6 +72,11 @@
                 std::unique_ptr<BaseTrackEventInternedDataIndex>>;
   std::array<InternedDataIndex, kMaxInternedDataFields> interned_data_indices =
       {};
+
+  // Track uuids for which we have written descriptors into the trace. If a
+  // trace event uses a track which is not in this set, we'll write out a
+  // descriptor for it.
+  base::FlatSet<uint64_t> seen_tracks;
 };
 
 // The backend portion of the track event trace point implemention. Outlined to
@@ -101,7 +108,36 @@
     WriteDebugAnnotation(annotation, value);
   }
 
+  // If the given track hasn't been seen by the trace writer yet, write a
+  // descriptor for it into the trace. Doesn't take a lock unless the track
+  // descriptor is new.
+  template <typename TrackType>
+  static void WriteTrackDescriptorIfNeeded(
+      const TrackType& track,
+      TraceWriterBase* trace_writer,
+      TrackEventIncrementalState* incr_state) {
+    auto it_and_inserted = incr_state->seen_tracks.insert(track.uuid);
+    if (PERFETTO_LIKELY(!it_and_inserted.second))
+      return;
+    WriteTrackDescriptor(track, trace_writer);
+  }
+
+  // Unconditionally write a track descriptor into the trace.
+  template <typename TrackType>
+  static void WriteTrackDescriptor(const TrackType& track,
+                                   TraceWriterBase* trace_writer) {
+    TrackRegistry::Get()->SerializeTrack(
+        track, NewTracePacket(trace_writer, GetTimeNs()));
+  }
+
  private:
+  static uint64_t GetTimeNs();
+  static void ResetIncrementalState(TraceWriterBase*, uint64_t timestamp);
+  static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
+      TraceWriterBase*,
+      uint64_t timestamp,
+      uint32_t seq_flags =
+          protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
   static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
       perfetto::EventContext*,
       const char* name);
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index f291186..bfe6692 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -25,6 +25,9 @@
 #include "perfetto/tracing/internal/track_event_data_source.h"
 #include "perfetto/tracing/track_event_category_registry.h"
 
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
 // Defines data structures for backing a category registry.
 //
 // Each category has one enabled/disabled bit per possible data source instance.
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 023e6f0..61e805c 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -75,6 +75,14 @@
   // Must be one of [4, 8, 16, 32].
   uint32_t shmem_page_size_hint_kb = 0;
 
+  bool operator==(const TracingInitArgs& other) const {
+    return std::tie(backends, custom_backend, platform, shmem_size_hint_kb,
+                    shmem_page_size_hint_kb, dcheck_is_on_) ==
+           std::tie(other.backends, other.custom_backend, other.platform,
+                    other.shmem_size_hint_kb, other.shmem_page_size_hint_kb,
+                    other.dcheck_is_on_);
+  }
+
  protected:
   friend class Tracing;
   bool dcheck_is_on_ = PERFETTO_DCHECK_IS_ON();
@@ -84,7 +92,7 @@
 class PERFETTO_EXPORT Tracing {
  public:
   // Initializes Perfetto with the given backends in the calling process and/or
-  // with a user-provided backend. Can only be called once.
+  // with a user-provided backend. No-op if called more than once.
   static void Initialize(const TracingInitArgs&);
 
   // Start a new tracing session using the given tracing backend. Use
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
new file mode 100644
index 0000000..7e56de4
--- /dev/null
+++ b/include/perfetto/tracing/track.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 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 INCLUDE_PERFETTO_TRACING_TRACK_H_
+#define INCLUDE_PERFETTO_TRACING_TRACK_H_
+
+#include "perfetto/base/export.h"
+#include "perfetto/base/proc_utils.h"
+#include "perfetto/base/thread_utils.h"
+#include "perfetto/protozero/message_handle.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
+
+#include <stdint.h>
+#include <map>
+#include <mutex>
+
+namespace perfetto {
+namespace internal {
+class TrackRegistry;
+}
+
+// Track events are recorded on a timeline track, which maintains the relative
+// time ordering of all events on that track. Each thread has its own default
+// track (ThreadTrack), which is by default where all track events are written.
+// Thread tracks are grouped under their hosting process (ProcessTrack).
+
+// Events which aren't strictly scoped to a thread or a process, or don't
+// correspond to synchronous code execution on a thread can use a custom
+// track (Track, ThreadTrack or ProcessTrack). A Track object can also
+// optionally be parented to a thread or a process.
+//
+// A track is represented by a uuid, which must be unique across the entire
+// recorded trace.
+//
+// For example, to record an event that begins and ends on different threads,
+// use a matching id to tie the begin and end events together:
+//
+//   TRACE_EVENT_BEGIN("category", "AsyncEvent", perfetto::Track(8086));
+//   ...
+//   TRACE_EVENT_END("category", perfetto::Track(8086));
+//
+// Tracks can also be annotated with metadata:
+//
+//   perfetto::TrackEvent::SetTrackDescriptor(
+//       track, [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+//         desc->set_name("MyTrack");
+//       });
+//
+// The metadata remains valid between tracing sessions. To free up data for a
+// track, call EraseTrackDescriptor:
+//
+//   perfetto::TrackEvent::EraseTrackDescriptor(track);
+//
+struct PERFETTO_EXPORT Track {
+  const uint64_t uuid;
+  const uint64_t parent_uuid;
+  constexpr Track() : uuid(0), parent_uuid(0) {}
+
+  // Construct a track with identifier |id|, optionally parented under |parent|.
+  // If no parent is specified, the track's parent is the current process's
+  // track.
+  //
+  // To minimize the chances for accidental id collisions across processes, the
+  // track's effective uuid is generated by xorring |id| with a random,
+  // per-process cookie.
+  explicit Track(uint64_t id, Track parent = MakeProcessTrack())
+      : uuid(id ^ parent.uuid), parent_uuid(parent.uuid) {}
+
+  explicit operator bool() const { return uuid; }
+  void Serialize(protos::pbzero::TrackDescriptor*) const;
+
+ protected:
+  static Track MakeThreadTrack(base::PlatformThreadId tid_) {
+    return Track(static_cast<uint64_t>(tid_), MakeProcessTrack());
+  }
+
+  static Track MakeProcessTrack() { return Track(process_uuid, Track()); }
+
+ private:
+  friend class internal::TrackRegistry;
+  static uint64_t process_uuid;
+};
+
+// A process track represents events that describe the state of the entire
+// application (e.g., counter events). Currently a ProcessTrack can only
+// represent the current process.
+struct PERFETTO_EXPORT ProcessTrack : public Track {
+  const base::PlatformProcessId pid;
+
+  static ProcessTrack Current() { return ProcessTrack(); }
+
+  void Serialize(protos::pbzero::TrackDescriptor*) const;
+
+ private:
+  ProcessTrack() : Track(MakeProcessTrack()), pid(base::GetProcessId()) {}
+};
+
+// A thread track is associated with a specific thread of execution. Currently
+// only threads in the current process can be referenced.
+struct PERFETTO_EXPORT ThreadTrack : public Track {
+  const base::PlatformProcessId pid;
+  const base::PlatformThreadId tid;
+
+  static ThreadTrack Current() { return ThreadTrack(base::GetThreadId()); }
+
+  // Represents a thread in the current process.
+  static ThreadTrack ForThread(base::PlatformThreadId tid_) {
+    return ThreadTrack(tid_);
+  }
+
+  void Serialize(protos::pbzero::TrackDescriptor*) const;
+
+ private:
+  explicit ThreadTrack(base::PlatformThreadId tid_)
+      : Track(MakeThreadTrack(tid_)),
+        pid(ProcessTrack::Current().pid),
+        tid(tid_) {}
+};
+
+namespace internal {
+
+// Keeps a map of uuids to serialized track descriptors and provides a
+// thread-safe way to read and write them. Each trace writer keeps a TLS set of
+// the tracks it has seen (see TrackEventIncrementalState). In the common case,
+// this registry is not consulted (and no locks are taken). However when a new
+// track is seen, this registry is used to write either 1) the default
+// descriptor for that track (see *Track::Serialize) or 2) a serialized
+// descriptor stored in the registry which may have additional metadata (e.g.,
+// track name).
+// TODO(eseckler): Remove PERFETTO_EXPORT once Chromium no longer calls
+// TrackRegistry::InitializeInstance() directly.
+class PERFETTO_EXPORT TrackRegistry {
+ public:
+  using SerializedTrackDescriptor = std::string;
+
+  TrackRegistry();
+  ~TrackRegistry();
+
+  static void InitializeInstance();
+  static TrackRegistry* Get() { return instance_; }
+
+  void EraseTrack(Track);
+
+  // Store metadata for |track| in the registry. |fill_function| is called
+  // synchronously to record additional properties for the track.
+  template <typename TrackType>
+  void UpdateTrack(
+      const TrackType& track,
+      std::function<void(protos::pbzero::TrackDescriptor*)> fill_function) {
+    UpdateTrackImpl(track, [&](protos::pbzero::TrackDescriptor* desc) {
+      track.Serialize(desc);
+      fill_function(desc);
+    });
+  }
+
+  // If |track| exists in the registry, write out the serialized track
+  // descriptor for it into |packet|. Otherwise just the ephemeral track object
+  // is serialized without any additional metadata.
+  template <typename TrackType>
+  void SerializeTrack(
+      const TrackType& track,
+      protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
+    // If the track has extra metadata (recorded with UpdateTrack), it will be
+    // found in the registry. To minimize the time the lock is held, make a copy
+    // of the data held in the registry and write it outside the lock.
+    std::string desc_copy;
+    {
+      std::lock_guard<std::mutex> lock(mutex_);
+      const auto& it = tracks_.find(track.uuid);
+      if (it != tracks_.end()) {
+        desc_copy = it->second;
+        PERFETTO_DCHECK(!desc_copy.empty());
+      }
+    }
+    if (!desc_copy.empty()) {
+      WriteTrackDescriptor(std::move(desc_copy), std::move(packet));
+    } else {
+      // Otherwise we just write the basic descriptor for this type of track
+      // (e.g., just uuid, no name).
+      track.Serialize(packet->set_track_descriptor());
+    }
+  }
+
+  static void WriteTrackDescriptor(
+      const SerializedTrackDescriptor& desc,
+      protozero::MessageHandle<protos::pbzero::TracePacket> packet);
+
+ private:
+  void UpdateTrackImpl(
+      Track,
+      std::function<void(protos::pbzero::TrackDescriptor*)> fill_function);
+
+  std::mutex mutex_;
+  std::map<uint64_t /* uuid */, SerializedTrackDescriptor> tracks_;
+
+  static TrackRegistry* instance_;
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_TRACK_H_
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index fa4776b..3d8e02f 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -21,6 +21,7 @@
 #include "perfetto/tracing/internal/track_event_data_source.h"
 #include "perfetto/tracing/internal/track_event_internal.h"
 #include "perfetto/tracing/internal/track_event_macros.h"
+#include "perfetto/tracing/track.h"
 #include "perfetto/tracing/track_event_category_registry.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
@@ -68,6 +69,14 @@
 //         });
 //       }
 //
+//  Note that track events must be nested consistently, i.e., the following is
+//  not allowed:
+//
+//    TRACE_EVENT_BEGIN("a", "bar", ...);
+//    TRACE_EVENT_BEGIN("b", "foo", ...);
+//    TRACE_EVENT_END("a");  // "foo" must be closed before "bar".
+//    TRACE_EVENT_END("b");
+//
 // ====================
 // Implementation notes
 // ====================
@@ -129,23 +138,31 @@
   PERFETTO_INTERNAL_DECLARE_CATEGORIES(__VA_ARGS__)            \
   /* The track event data source for this set of categories */ \
   PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE();         \
-  }  // namespace PERFETTO_TRACK_EVENT_NAMESPACE
+  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */             \
+  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
 
 // Allocate storage for each category by using this macro once per track event
 // namespace.
-#define PERFETTO_TRACK_EVENT_STATIC_STORAGE() \
-  namespace PERFETTO_TRACK_EVENT_NAMESPACE {  \
-  PERFETTO_INTERNAL_CATEGORY_STORAGE()        \
-  }  // namespace PERFETTO_TRACK_EVENT_NAMESPACE
+#define PERFETTO_TRACK_EVENT_STATIC_STORAGE()      \
+  namespace PERFETTO_TRACK_EVENT_NAMESPACE {       \
+  PERFETTO_INTERNAL_CATEGORY_STORAGE()             \
+  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */ \
+  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
 
-// Begin a thread-scoped slice under |category| with the title |name|. Both
-// strings must be static constants. The track event is only recorded if
-// |category| is enabled for a tracing session.
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
+// Begin a slice under |category| with the title |name|. Both strings must be
+// static constants. The track event is only recorded if |category| is enabled
+// for a tracing session.
 //
 // |name| must be a string with static lifetime (i.e., the same
 // address must not be used for a different event name in the future). If you
 // want to use a dynamically allocated name, do this:
 //
+// The slice is thread-scoped (i.e., written to the default track of the current
+// thread) unless overridden with a custom track object (see Track).
+//
 //  TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) {
 //    ctx.event()->set_name(dynamic_name);
 //  });
@@ -155,18 +172,17 @@
       category, name,                          \
       ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN, ##__VA_ARGS__)
 
-// End a thread-scoped slice under |category|.
+// End a slice under |category|.
 #define TRACE_EVENT_END(category, ...) \
   PERFETTO_INTERNAL_TRACK_EVENT(       \
       category, nullptr,               \
       ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END, ##__VA_ARGS__)
 
-// Begin a thread-scoped slice which gets automatically closed when going out of
-// scope.
+// Begin a slice which gets automatically closed when going out of scope.
 #define TRACE_EVENT(category, name, ...) \
   PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ##__VA_ARGS__)
 
-// Emit a thread-scoped slice which has zero duration.
+// Emit a slice which has zero duration.
 // TODO(skyostil): Add support for process-wide and global instant events.
 #define TRACE_EVENT_INSTANT(category, name, ...)                            \
   PERFETTO_INTERNAL_TRACK_EVENT(                                            \
diff --git a/include/perfetto/tracing/track_event_legacy.h b/include/perfetto/tracing/track_event_legacy.h
new file mode 100644
index 0000000..bf83ca4
--- /dev/null
+++ b/include/perfetto/tracing/track_event_legacy.h
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2020 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 INCLUDE_PERFETTO_TRACING_TRACK_EVENT_LEGACY_H_
+#define INCLUDE_PERFETTO_TRACING_TRACK_EVENT_LEGACY_H_
+
+// This file defines a compatibility shim between legacy (Chrome, V8) trace
+// event macros and track events. To avoid accidentally introducing legacy
+// events in new code, the PERFETTO_ENABLE_LEGACY_TRACE_EVENTS macro must be set
+// to 1 activate the compatibility layer.
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/tracing/track_event.h"
+
+#include <stdint.h>
+
+#ifndef PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 0
+#endif
+
+#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
+// ----------------------------------------------------------------------------
+// Constants.
+// ----------------------------------------------------------------------------
+
+// The following constants are defined in the global namespace, since they were
+// originally implemented as macros.
+
+// Event phases.
+static constexpr char TRACE_EVENT_PHASE_BEGIN = 'B';
+static constexpr char TRACE_EVENT_PHASE_END = 'E';
+static constexpr char TRACE_EVENT_PHASE_COMPLETE = 'X';
+static constexpr char TRACE_EVENT_PHASE_INSTANT = 'I';
+static constexpr char TRACE_EVENT_PHASE_ASYNC_BEGIN = 'S';
+static constexpr char TRACE_EVENT_PHASE_ASYNC_STEP_INTO = 'T';
+static constexpr char TRACE_EVENT_PHASE_ASYNC_STEP_PAST = 'p';
+static constexpr char TRACE_EVENT_PHASE_ASYNC_END = 'F';
+static constexpr char TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN = 'b';
+static constexpr char TRACE_EVENT_PHASE_NESTABLE_ASYNC_END = 'e';
+static constexpr char TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT = 'n';
+static constexpr char TRACE_EVENT_PHASE_FLOW_BEGIN = 's';
+static constexpr char TRACE_EVENT_PHASE_FLOW_STEP = 't';
+static constexpr char TRACE_EVENT_PHASE_FLOW_END = 'f';
+static constexpr char TRACE_EVENT_PHASE_METADATA = 'M';
+static constexpr char TRACE_EVENT_PHASE_COUNTER = 'C';
+static constexpr char TRACE_EVENT_PHASE_SAMPLE = 'P';
+static constexpr char TRACE_EVENT_PHASE_CREATE_OBJECT = 'N';
+static constexpr char TRACE_EVENT_PHASE_SNAPSHOT_OBJECT = 'O';
+static constexpr char TRACE_EVENT_PHASE_DELETE_OBJECT = 'D';
+static constexpr char TRACE_EVENT_PHASE_MEMORY_DUMP = 'v';
+static constexpr char TRACE_EVENT_PHASE_MARK = 'R';
+static constexpr char TRACE_EVENT_PHASE_CLOCK_SYNC = 'c';
+static constexpr char TRACE_EVENT_PHASE_ENTER_CONTEXT = '(';
+static constexpr char TRACE_EVENT_PHASE_LEAVE_CONTEXT = ')';
+
+// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
+static constexpr uint32_t TRACE_EVENT_FLAG_NONE = 0;
+static constexpr uint32_t TRACE_EVENT_FLAG_COPY = 1u << 0;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_ID = 1u << 1;
+// TODO(crbug.com/639003): Free this bit after ID mangling is deprecated.
+static constexpr uint32_t TRACE_EVENT_FLAG_MANGLE_ID = 1u << 2;
+static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_OFFSET = 1u << 3;
+static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_EXTRA = 1u << 4;
+static constexpr uint32_t TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP = 1u << 5;
+static constexpr uint32_t TRACE_EVENT_FLAG_ASYNC_TTS = 1u << 6;
+static constexpr uint32_t TRACE_EVENT_FLAG_BIND_TO_ENCLOSING = 1u << 7;
+static constexpr uint32_t TRACE_EVENT_FLAG_FLOW_IN = 1u << 8;
+static constexpr uint32_t TRACE_EVENT_FLAG_FLOW_OUT = 1u << 9;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_CONTEXT_ID = 1u << 10;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_PROCESS_ID = 1u << 11;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_LOCAL_ID = 1u << 12;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_GLOBAL_ID = 1u << 13;
+// TODO(eseckler): Remove once we have native support for typed proto events in
+// TRACE_EVENT macros.
+static constexpr uint32_t TRACE_EVENT_FLAG_TYPED_PROTO_ARGS = 1u << 15;
+static constexpr uint32_t TRACE_EVENT_FLAG_JAVA_STRING_LITERALS = 1u << 16;
+
+static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_MASK =
+    TRACE_EVENT_FLAG_SCOPE_OFFSET | TRACE_EVENT_FLAG_SCOPE_EXTRA;
+
+// Type values for identifying types in the TraceValue union.
+static constexpr uint8_t TRACE_VALUE_TYPE_BOOL = 1;
+static constexpr uint8_t TRACE_VALUE_TYPE_UINT = 2;
+static constexpr uint8_t TRACE_VALUE_TYPE_INT = 3;
+static constexpr uint8_t TRACE_VALUE_TYPE_DOUBLE = 4;
+static constexpr uint8_t TRACE_VALUE_TYPE_POINTER = 5;
+static constexpr uint8_t TRACE_VALUE_TYPE_STRING = 6;
+static constexpr uint8_t TRACE_VALUE_TYPE_COPY_STRING = 7;
+static constexpr uint8_t TRACE_VALUE_TYPE_CONVERTABLE = 8;
+
+// Enum reflecting the scope of an INSTANT event. Must fit within
+// TRACE_EVENT_FLAG_SCOPE_MASK.
+static constexpr uint8_t TRACE_EVENT_SCOPE_GLOBAL = 0u << 3;
+static constexpr uint8_t TRACE_EVENT_SCOPE_PROCESS = 1u << 3;
+static constexpr uint8_t TRACE_EVENT_SCOPE_THREAD = 2u << 3;
+
+static constexpr char TRACE_EVENT_SCOPE_NAME_GLOBAL = 'g';
+static constexpr char TRACE_EVENT_SCOPE_NAME_PROCESS = 'p';
+static constexpr char TRACE_EVENT_SCOPE_NAME_THREAD = 't';
+
+// ----------------------------------------------------------------------------
+// Internal legacy trace point implementation.
+// ----------------------------------------------------------------------------
+
+namespace perfetto {
+namespace internal {
+
+class TrackEventLegacy {
+ public:
+  static constexpr protos::pbzero::TrackEvent::Type PhaseToType(char phase) {
+    // clang-format off
+    return (phase == TRACE_EVENT_PHASE_BEGIN) ?
+               protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN :
+           (phase == TRACE_EVENT_PHASE_END) ?
+               protos::pbzero::TrackEvent::TYPE_SLICE_END :
+           (phase == TRACE_EVENT_PHASE_INSTANT) ?
+               protos::pbzero::TrackEvent::TYPE_INSTANT :
+           protos::pbzero::TrackEvent::TYPE_UNSPECIFIED;
+    // clang-format on
+  }
+
+  // Reduce binary size overhead by outlining most of the code for writing a
+  // legacy trace event.
+  template <typename... Args>
+  static void WriteLegacyEvent(EventContext ctx,
+                               char phase,
+                               uint32_t flags,
+                               Args&&... args) PERFETTO_NO_INLINE {
+    AddDebugAnnotations(&ctx, std::forward<Args>(args)...);
+    SetTrackIfNeeded(&ctx, flags);
+    if (PhaseToType(phase) == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED) {
+      auto legacy_event = ctx.event()->set_legacy_event();
+      legacy_event->set_phase(phase);
+    }
+  }
+
+  // No arguments.
+  static void AddDebugAnnotations(EventContext*) {}
+
+  // One argument.
+  template <typename ArgType>
+  static void AddDebugAnnotations(EventContext* ctx,
+                                  const char* arg_name,
+                                  ArgType&& arg_value) {
+    TrackEventInternal::AddDebugAnnotation(ctx, arg_name, arg_value);
+  }
+
+  // Two arguments.
+  template <typename ArgType, typename ArgType2>
+  static void AddDebugAnnotations(EventContext* ctx,
+                                  const char* arg_name,
+                                  ArgType&& arg_value,
+                                  const char* arg_name2,
+                                  ArgType2&& arg_value2) {
+    TrackEventInternal::AddDebugAnnotation(ctx, arg_name, arg_value);
+    TrackEventInternal::AddDebugAnnotation(ctx, arg_name2, arg_value2);
+  }
+
+ private:
+  static void SetTrackIfNeeded(EventContext* ctx, uint32_t flags) {
+    auto scope = flags & TRACE_EVENT_FLAG_SCOPE_MASK;
+    switch (scope) {
+      case TRACE_EVENT_SCOPE_GLOBAL:
+        ctx->event()->set_track_uuid(0);
+        break;
+      case TRACE_EVENT_SCOPE_PROCESS:
+        ctx->event()->set_track_uuid(ProcessTrack::Current().uuid);
+        break;
+      default:
+      case TRACE_EVENT_SCOPE_THREAD:
+        // Thread scope is already the default.
+        break;
+    }
+  }
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+// A black hole trace point where unsupported trace events are routed.
+#define PERFETTO_INTERNAL_EVENT_NOOP(cat, name, ...) \
+  do {                                               \
+    if (false) {                                     \
+      ::perfetto::base::ignore_result(cat);          \
+      ::perfetto::base::ignore_result(name);         \
+    }                                                \
+  } while (false)
+
+// Implementations for the INTERNAL_* adapter macros used by the trace points
+// below.
+#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...)      \
+  PERFETTO_INTERNAL_TRACK_EVENT(                                         \
+      category, name,                                                    \
+      ::perfetto::internal::TrackEventLegacy::PhaseToType(phase),        \
+      [&](perfetto::EventContext ctx) {                                  \
+        using ::perfetto::internal::TrackEventLegacy;                    \
+        TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags, \
+                                           ##__VA_ARGS__);               \
+      })
+
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...)        \
+  PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(                             \
+      category, name, [&](perfetto::EventContext ctx) {             \
+        using ::perfetto::internal::TrackEventLegacy;               \
+        TrackEventLegacy::AddDebugAnnotations(&ctx, ##__VA_ARGS__); \
+      })
+
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(...) \
+  PERFETTO_INTERNAL_EVENT_NOOP(__VA_ARGS__)
+#define INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(...) \
+  PERFETTO_INTERNAL_EVENT_NOOP(__VA_ARGS__)
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(...) \
+  PERFETTO_INTERNAL_EVENT_NOOP(__VA_ARGS__)
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(...) \
+  PERFETTO_INTERNAL_EVENT_NOOP(__VA_ARGS__)
+#define INTERNAL_TRACE_EVENT_METADATA_ADD(...) \
+  PERFETTO_INTERNAL_EVENT_NOOP(__VA_ARGS__)
+
+#define INTERNAL_TRACE_TIME_TICKS_NOW() 0
+#define INTERNAL_TRACE_TIME_NOW() 0
+
+// ----------------------------------------------------------------------------
+// Legacy tracing common API (adapted from trace_event_common.h).
+// ----------------------------------------------------------------------------
+
+#define TRACE_DISABLED_BY_DEFAULT(name) "disabled-by-default-" name
+
+// Scoped events.
+#define TRACE_EVENT0(category_group, name) \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name)
+#define TRACE_EVENT_WITH_FLOW0(category_group, name, bind_id, flow_flags)  \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, bind_id, \
+                                            flow_flags)
+#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, arg1_name, arg1_val)
+#define TRACE_EVENT_WITH_FLOW1(category_group, name, bind_id, flow_flags,  \
+                               arg1_name, arg1_val)                        \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, bind_id, \
+                                            flow_flags, arg1_name, arg1_val)
+#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name,   \
+                     arg2_val)                                               \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, arg1_name, arg1_val, \
+                                  arg2_name, arg2_val)
+#define TRACE_EVENT_WITH_FLOW2(category_group, name, bind_id, flow_flags,    \
+                               arg1_name, arg1_val, arg2_name, arg2_val)     \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, bind_id,   \
+                                            flow_flags, arg1_name, arg1_val, \
+                                            arg2_name, arg2_val)
+
+// Instant events.
+#define TRACE_EVENT_INSTANT0(category_group, name, scope)                   \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE | scope)
+#define TRACE_EVENT_INSTANT1(category_group, name, scope, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
+                           TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val)
+#define TRACE_EVENT_INSTANT2(category_group, name, scope, arg1_name, arg1_val, \
+                             arg2_name, arg2_val)                              \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
+                           TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val, \
+                           arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_INSTANT0(category_group, name, scope)              \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \
+                           TRACE_EVENT_FLAG_COPY | scope)
+#define TRACE_EVENT_COPY_INSTANT1(category_group, name, scope, arg1_name,   \
+                                  arg1_val)                                 \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \
+                           TRACE_EVENT_FLAG_COPY | scope, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_INSTANT2(category_group, name, scope, arg1_name,      \
+                                  arg1_val, arg2_name, arg2_val)               \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
+                           TRACE_EVENT_FLAG_COPY | scope, arg1_name, arg1_val, \
+                           arg2_name, arg2_val)
+#define TRACE_EVENT_INSTANT_WITH_FLAGS0(category_group, name, scope_and_flags) \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
+                           scope_and_flags)
+#define TRACE_EVENT_INSTANT_WITH_FLAGS1(category_group, name, scope_and_flags, \
+                                        arg1_name, arg1_val)                   \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
+                           scope_and_flags, arg1_name, arg1_val)
+
+// Instant events with explicit timestamps.
+#define TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(category_group, name, scope,   \
+                                            timestamp)                     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(TRACE_EVENT_PHASE_INSTANT,       \
+                                          category_group, name, timestamp, \
+                                          TRACE_EVENT_FLAG_NONE | scope)
+
+#define TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(category_group, name, scope,  \
+                                            timestamp, arg_name, arg_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                \
+      TRACE_EVENT_PHASE_INSTANT, category_group, name, timestamp,         \
+      TRACE_EVENT_FLAG_NONE | scope, arg_name, arg_val)
+
+// Begin events.
+#define TRACE_EVENT_BEGIN0(category_group, name)                          \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_BEGIN1(category_group, name, arg1_name, arg1_val)     \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_BEGIN2(category_group, name, arg1_name, arg1_val,     \
+                           arg2_name, arg2_val)                           \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val,    \
+                           arg2_name, arg2_val)
+#define TRACE_EVENT_BEGIN_WITH_FLAGS0(category_group, name, flags) \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, flags)
+#define TRACE_EVENT_BEGIN_WITH_FLAGS1(category_group, name, flags, arg1_name, \
+                                      arg1_val)                               \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name,     \
+                           flags, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_BEGIN2(category_group, name, arg1_name, arg1_val, \
+                                arg2_name, arg2_val)                       \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name,  \
+                           TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val,     \
+                           arg2_name, arg2_val)
+
+// Begin events with explicit timestamps.
+#define TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(category_group, name, id, \
+                                                     thread_id, timestamp)     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                          \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id,      \
+      timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(                \
+    category_group, name, id, thread_id, timestamp)                       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                     \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \
+      timestamp, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP1(                \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val)  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                     \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \
+      timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2(                \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val,  \
+    arg2_name, arg2_val)                                                  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                     \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \
+      timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name,   \
+      arg2_val)
+
+// End events.
+#define TRACE_EVENT_END0(category_group, name)                          \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_END1(category_group, name, arg1_name, arg1_val)     \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_END2(category_group, name, arg1_name, arg1_val, arg2_name, \
+                         arg2_val)                                             \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name,        \
+                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val,         \
+                           arg2_name, arg2_val)
+#define TRACE_EVENT_END_WITH_FLAGS0(category_group, name, flags) \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, flags)
+#define TRACE_EVENT_END_WITH_FLAGS1(category_group, name, flags, arg1_name,    \
+                                    arg1_val)                                  \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, flags, \
+                           arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_END2(category_group, name, arg1_name, arg1_val, \
+                              arg2_name, arg2_val)                       \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name,  \
+                           TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val,   \
+                           arg2_name, arg2_val)
+
+// Mark events.
+#define TRACE_EVENT_MARK_WITH_TIMESTAMP0(category_group, name, timestamp)  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(TRACE_EVENT_PHASE_MARK,          \
+                                          category_group, name, timestamp, \
+                                          TRACE_EVENT_FLAG_NONE)
+
+#define TRACE_EVENT_MARK_WITH_TIMESTAMP1(category_group, name, timestamp, \
+                                         arg1_name, arg1_val)             \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                \
+      TRACE_EVENT_PHASE_MARK, category_group, name, timestamp,            \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+
+#define TRACE_EVENT_MARK_WITH_TIMESTAMP2(                                      \
+    category_group, name, timestamp, arg1_name, arg1_val, arg2_name, arg2_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                     \
+      TRACE_EVENT_PHASE_MARK, category_group, name, timestamp,                 \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+
+#define TRACE_EVENT_COPY_MARK(category_group, name)                      \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_MARK, category_group, name, \
+                           TRACE_EVENT_FLAG_COPY)
+
+#define TRACE_EVENT_COPY_MARK1(category_group, name, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_MARK, category_group, name,  \
+                           TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+
+#define TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(category_group, name, timestamp) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(TRACE_EVENT_PHASE_MARK,             \
+                                          category_group, name, timestamp,    \
+                                          TRACE_EVENT_FLAG_COPY)
+
+// End events with explicit thread and timestamp.
+#define TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(category_group, name, id, \
+                                                   thread_id, timestamp)     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id,      \
+      timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0(                \
+    category_group, name, id, thread_id, timestamp)                     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                   \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \
+      timestamp, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP1(                 \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id,  \
+      timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2(                 \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \
+    arg2_name, arg2_val)                                                 \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id,  \
+      timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name,  \
+      arg2_val)
+
+// Counters.
+#define TRACE_COUNTER1(category_group, name, value)                         \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE, "value",                  \
+                           static_cast<int>(value))
+#define TRACE_COUNTER_WITH_FLAG1(category_group, name, flag, value)         \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \
+                           flag, "value", static_cast<int>(value))
+#define TRACE_COPY_COUNTER1(category_group, name, value)                    \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \
+                           TRACE_EVENT_FLAG_COPY, "value",                  \
+                           static_cast<int>(value))
+#define TRACE_COUNTER2(category_group, name, value1_name, value1_val,       \
+                       value2_name, value2_val)                             \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \
+                           TRACE_EVENT_FLAG_NONE, value1_name,              \
+                           static_cast<int>(value1_val), value2_name,       \
+                           static_cast<int>(value2_val))
+#define TRACE_COPY_COUNTER2(category_group, name, value1_name, value1_val,  \
+                            value2_name, value2_val)                        \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \
+                           TRACE_EVENT_FLAG_COPY, value1_name,              \
+                           static_cast<int>(value1_val), value2_name,       \
+                           static_cast<int>(value2_val))
+
+// Counters with explicit timestamps.
+#define TRACE_COUNTER_WITH_TIMESTAMP1(category_group, name, timestamp, value) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                    \
+      TRACE_EVENT_PHASE_COUNTER, category_group, name, timestamp,             \
+      TRACE_EVENT_FLAG_NONE, "value", static_cast<int>(value))
+
+#define TRACE_COUNTER_WITH_TIMESTAMP2(category_group, name, timestamp,      \
+                                      value1_name, value1_val, value2_name, \
+                                      value2_val)                           \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                  \
+      TRACE_EVENT_PHASE_COUNTER, category_group, name, timestamp,           \
+      TRACE_EVENT_FLAG_NONE, value1_name, static_cast<int>(value1_val),     \
+      value2_name, static_cast<int>(value2_val))
+
+// Counters with ids.
+#define TRACE_COUNTER_ID1(category_group, name, id, value)                    \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_NONE, "value",  \
+                                   static_cast<int>(value))
+#define TRACE_COPY_COUNTER_ID1(category_group, name, id, value)               \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_COPY, "value",  \
+                                   static_cast<int>(value))
+#define TRACE_COUNTER_ID2(category_group, name, id, value1_name, value1_val,  \
+                          value2_name, value2_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_NONE,           \
+                                   value1_name, static_cast<int>(value1_val), \
+                                   value2_name, static_cast<int>(value2_val))
+#define TRACE_COPY_COUNTER_ID2(category_group, name, id, value1_name,         \
+                               value1_val, value2_name, value2_val)           \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_COPY,           \
+                                   value1_name, static_cast<int>(value1_val), \
+                                   value2_name, static_cast<int>(value2_val))
+
+// Sampling profiler events.
+#define TRACE_EVENT_SAMPLE_WITH_ID1(category_group, name, id, arg1_name,       \
+                                    arg1_val)                                  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_SAMPLE, category_group,   \
+                                   name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
+                                   arg1_val)
+
+// Legacy async events.
+#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+                                   category_group, name, id,      \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_BEGIN1(category_group, name, id, arg1_name, \
+                                 arg1_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,     \
+                                   category_group, name, id,          \
+                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_BEGIN2(category_group, name, id, arg1_name, \
+                                 arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                   \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id,        \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category_group, name, id)   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+                                   category_group, name, id,      \
+                                   TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category_group, name, id, arg1_name, \
+                                      arg1_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,          \
+                                   category_group, name, id,               \
+                                   TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category_group, name, id, arg1_name, \
+                                      arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id,             \
+      TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_ASYNC_BEGIN_WITH_FLAGS0(category_group, name, id, flags) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,            \
+                                   category_group, name, id, flags)
+
+// Legacy async events with explicit timestamps.
+#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, id, \
+                                                timestamp)                \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                     \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id,            \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1(                           \
+    category_group, name, id, timestamp, arg1_name, arg1_val)              \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                      \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id,             \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
+      arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP2(category_group, name, id,      \
+                                                timestamp, arg1_name,          \
+                                                arg1_val, arg2_name, arg2_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                          \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id,                 \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE,     \
+      arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, id, \
+                                                     timestamp)                \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                          \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id,                 \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP_AND_FLAGS0(     \
+    category_group, name, id, timestamp, flags)                \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(          \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, flags)
+
+// Legacy async step into events.
+#define TRACE_EVENT_ASYNC_STEP_INTO0(category_group, name, id, step)  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, \
+                                   category_group, name, id,          \
+                                   TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_ASYNC_STEP_INTO1(category_group, name, id, step, \
+                                     arg1_name, arg1_val)            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                  \
+      TRACE_EVENT_PHASE_ASYNC_STEP_INTO, category_group, name, id,   \
+      TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val)
+
+// Legacy async step into events with timestamps.
+#define TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category_group, name, id, \
+                                                    step, timestamp)          \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
+      TRACE_EVENT_PHASE_ASYNC_STEP_INTO, category_group, name, id,            \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE,    \
+      "step", step)
+
+// Legacy async step past events.
+#define TRACE_EVENT_ASYNC_STEP_PAST0(category_group, name, id, step)  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, \
+                                   category_group, name, id,          \
+                                   TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_ASYNC_STEP_PAST1(category_group, name, id, step, \
+                                     arg1_name, arg1_val)            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                  \
+      TRACE_EVENT_PHASE_ASYNC_STEP_PAST, category_group, name, id,   \
+      TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val)
+
+// Legacy async end events.
+#define TRACE_EVENT_ASYNC_END0(category_group, name, id)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+                                   category_group, name, id,    \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_END1(category_group, name, id, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,               \
+                                   category_group, name, id,                  \
+                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_END2(category_group, name, id, arg1_name, arg1_val, \
+                               arg2_name, arg2_val)                           \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                           \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,                  \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_END0(category_group, name, id)   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+                                   category_group, name, id,    \
+                                   TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_ASYNC_END1(category_group, name, id, arg1_name, \
+                                    arg1_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,          \
+                                   category_group, name, id,             \
+                                   TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_END2(category_group, name, id, arg1_name, \
+                                    arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,             \
+      TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_ASYNC_END_WITH_FLAGS0(category_group, name, id, flags) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,            \
+                                   category_group, name, id, flags)
+
+// Legacy async end events with explicit timestamps.
+#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(category_group, name, id, \
+                                              timestamp)                \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                   \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,            \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP1(category_group, name, id,       \
+                                              timestamp, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,                  \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE,    \
+      arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP2(category_group, name, id,       \
+                                              timestamp, arg1_name, arg1_val, \
+                                              arg2_name, arg2_val)            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,                  \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE,    \
+      arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_END_WITH_TIMESTAMP0(category_group, name, id, \
+                                                   timestamp)                \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,                 \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP_AND_FLAGS0(category_group, name, \
+                                                        id, timestamp, flags) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id,                  \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, flags)
+
+// Async events.
+#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(category_group, name, id)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \
+                                   category_group, name, id,               \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category_group, name, id, arg1_name, \
+                                          arg1_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN,     \
+                                   category_group, name, id,                   \
+                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(category_group, name, id, arg1_name, \
+                                          arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id,        \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Async end events.
+#define TRACE_EVENT_NESTABLE_ASYNC_END0(category_group, name, id)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \
+                                   category_group, name, id,             \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_NESTABLE_ASYNC_END1(category_group, name, id, arg1_name, \
+                                        arg1_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END,     \
+                                   category_group, name, id,                 \
+                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_NESTABLE_ASYNC_END2(category_group, name, id, arg1_name, \
+                                        arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id,        \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Async instant events.
+#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT0(category_group, name, id)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \
+                                   category_group, name, id,                 \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(category_group, name, id,        \
+                                            arg1_name, arg1_val)             \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \
+                                   category_group, name, id,                 \
+                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT2(                              \
+    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val)   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                       \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, category_group, name, id, \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TTS2(                       \
+    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id,        \
+      TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+      arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TTS2(                         \
+    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id,          \
+      TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+      arg2_name, arg2_val)
+
+// Async events with explicit timestamps.
+#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, \
+                                                         id, timestamp)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                          \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id,        \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(category_group, name, \
+                                                       id, timestamp)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id,        \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP1(                    \
+    category_group, name, id, timestamp, arg1_name, arg1_val)              \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                      \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id,      \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
+      arg1_name, arg1_val)
+#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT_WITH_TIMESTAMP0(               \
+    category_group, name, id, timestamp)                                  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                     \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, category_group, name, id, \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(          \
+    category_group, name, id, timestamp)                                \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                   \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(          \
+    category_group, name, id, timestamp)                              \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                 \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY)
+
+// Legacy flow events.
+#define TRACE_EVENT_FLOW_BEGIN0(category_group, name, id)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+                                   category_group, name, id,     \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_BEGIN1(category_group, name, id, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN,               \
+                                   category_group, name, id,                   \
+                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_FLOW_BEGIN2(category_group, name, id, arg1_name, arg1_val, \
+                                arg2_name, arg2_val)                           \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
+      TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, id,                  \
+      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_FLOW_BEGIN0(category_group, name, id)   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+                                   category_group, name, id,     \
+                                   TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_FLOW_BEGIN1(category_group, name, id, arg1_name, \
+                                     arg1_val)                            \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN,          \
+                                   category_group, name, id,              \
+                                   TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_BEGIN2(category_group, name, id, arg1_name, \
+                                     arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                       \
+      TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, id,             \
+      TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Legacy flow step events.
+#define TRACE_EVENT_FLOW_STEP0(category_group, name, id, step)  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+                                   category_group, name, id,    \
+                                   TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_FLOW_STEP1(category_group, name, id, step, arg1_name, \
+                               arg1_val)                                  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                       \
+      TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, id,              \
+      TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_STEP0(category_group, name, id, step) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP,     \
+                                   category_group, name, id,        \
+                                   TRACE_EVENT_FLAG_COPY, "step", step)
+#define TRACE_EVENT_COPY_FLOW_STEP1(category_group, name, id, step, arg1_name, \
+                                    arg1_val)                                  \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                            \
+      TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, id,                   \
+      TRACE_EVENT_FLAG_COPY, "step", step, arg1_name, arg1_val)
+
+// Legacy flow end events.
+#define TRACE_EVENT_FLOW_END0(category_group, name, id)                        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0(category_group, name, id)      \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id,                                   \
+                                   TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
+#define TRACE_EVENT_FLOW_END1(category_group, name, id, arg1_name, arg1_val)   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
+                                   arg1_val)
+#define TRACE_EVENT_FLOW_END2(category_group, name, id, arg1_name, arg1_val,   \
+                              arg2_name, arg2_val)                             \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
+                                   arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_FLOW_END0(category_group, name, id)                   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_FLOW_END1(category_group, name, id, arg1_name,        \
+                                   arg1_val)                                   \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \
+                                   arg1_val)
+#define TRACE_EVENT_COPY_FLOW_END2(category_group, name, id, arg1_name,        \
+                                   arg1_val, arg2_name, arg2_val)              \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+                                   name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \
+                                   arg1_val, arg2_name, arg2_val)
+
+// Special strongly typed trace events.
+// TODO(skyostil): Migrate these to regular track event trace points.
+#define TRACE_TASK_EXECUTION(run_function, task) \
+  if (false) {                                   \
+    base::ignore_result(run_function);           \
+    base::ignore_result(task);                   \
+  }
+
+#define TRACE_LOG_MESSAGE(file, message, line) \
+  if (false) {                                 \
+    base::ignore_result(file);                 \
+    base::ignore_result(message);              \
+    base::ignore_result(line);                 \
+  }
+
+// Metadata events.
+#define TRACE_EVENT_METADATA1(category_group, name, arg1_name, arg1_val) \
+  INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, arg1_name, arg1_val)
+
+// Clock sync events.
+#define TRACE_EVENT_CLOCK_SYNC_RECEIVER(sync_id)                           \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_CLOCK_SYNC, "__metadata",     \
+                           "clock_sync", TRACE_EVENT_FLAG_NONE, "sync_id", \
+                           sync_id)
+#define TRACE_EVENT_CLOCK_SYNC_ISSUER(sync_id, issue_ts, issue_end_ts)        \
+  INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                    \
+      TRACE_EVENT_PHASE_CLOCK_SYNC, "__metadata", "clock_sync", issue_end_ts, \
+      TRACE_EVENT_FLAG_NONE, "sync_id", sync_id, "issue_ts", issue_ts)
+
+// Object events.
+#define TRACE_EVENT_OBJECT_CREATED_WITH_ID(category_group, name, id) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_CREATE_OBJECT,  \
+                                   category_group, name, id,         \
+                                   TRACE_EVENT_FLAG_NONE)
+
+#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(category_group, name, id, \
+                                            snapshot)                 \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                   \
+      TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, id,    \
+      TRACE_EVENT_FLAG_NONE, "snapshot", snapshot)
+
+#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP(                 \
+    category_group, name, id, timestamp, snapshot)                         \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                      \
+      TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, id,         \
+      TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
+      "snapshot", snapshot)
+
+#define TRACE_EVENT_OBJECT_DELETED_WITH_ID(category_group, name, id) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_DELETE_OBJECT,  \
+                                   category_group, name, id,         \
+                                   TRACE_EVENT_FLAG_NONE)
+
+// Context events.
+#define TRACE_EVENT_ENTER_CONTEXT(category_group, name, context)    \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ENTER_CONTEXT, \
+                                   category_group, name, context,   \
+                                   TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_LEAVE_CONTEXT(category_group, name, context)    \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_LEAVE_CONTEXT, \
+                                   category_group, name, context,   \
+                                   TRACE_EVENT_FLAG_NONE)
+
+// Macro to efficiently determine if a given category group is enabled.
+// TODO(skyostil): Implement.
+#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \
+  do {                                                          \
+    *ret = false;                                               \
+  } while (0)
+
+// Macro to efficiently determine, through polling, if a new trace has begun.
+// TODO(skyostil): Implement.
+#define TRACE_EVENT_IS_NEW_TRACE(ret) \
+  do {                                \
+    *ret = false;                     \
+  } while (0)
+
+// Time queries.
+#define TRACE_TIME_TICKS_NOW() INTERNAL_TRACE_TIME_TICKS_NOW()
+#define TRACE_TIME_NOW() INTERNAL_TRACE_TIME_NOW()
+
+// ----------------------------------------------------------------------------
+// Legacy tracing API (adapted from trace_event.h).
+// ----------------------------------------------------------------------------
+
+// We can implement the following subset of the legacy tracing API without
+// involvement from the embedder. APIs such as TraceId and
+// TRACE_EVENT_API_ADD_TRACE_EVENT are still up to the embedder to define.
+
+#define TRACE_STR_COPY(str) (str)
+
+// TODO(skyostil): Implement properly using CategoryRegistry.
+#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category) \
+  [&] {                                                      \
+    static uint8_t enabled;                                  \
+    TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &enabled);  \
+    return &enabled;                                         \
+  }()
+
+#endif  // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+
+#endif  // INCLUDE_PERFETTO_TRACING_TRACK_EVENT_LEGACY_H_
diff --git a/protos/perfetto/common/android_log_constants.proto b/protos/perfetto/common/android_log_constants.proto
index 04bd728..47e42d1 100644
--- a/protos/perfetto/common/android_log_constants.proto
+++ b/protos/perfetto/common/android_log_constants.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/commit_data_request.proto b/protos/perfetto/common/commit_data_request.proto
index 59e29fb..7e6a1a9 100644
--- a/protos/perfetto/common/commit_data_request.proto
+++ b/protos/perfetto/common/commit_data_request.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/data_source_descriptor.proto b/protos/perfetto/common/data_source_descriptor.proto
index 63a5114..725c2f2 100644
--- a/protos/perfetto/common/data_source_descriptor.proto
+++ b/protos/perfetto/common/data_source_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/descriptor.proto b/protos/perfetto/common/descriptor.proto
index d0d86ab..95f6fc0 100644
--- a/protos/perfetto/common/descriptor.proto
+++ b/protos/perfetto/common/descriptor.proto
@@ -19,7 +19,6 @@
 
 package perfetto.protos;
 
-option optimize_for = LITE_RUNTIME;
 
 // The protocol compiler can output a FileDescriptorSet containing the .proto
 // files it parses.
diff --git a/protos/perfetto/common/gpu_counter_descriptor.proto b/protos/perfetto/common/gpu_counter_descriptor.proto
index f32b012..7a81e1a 100644
--- a/protos/perfetto/common/gpu_counter_descriptor.proto
+++ b/protos/perfetto/common/gpu_counter_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/observable_events.proto b/protos/perfetto/common/observable_events.proto
index e1256f8..e3911e4 100644
--- a/protos/perfetto/common/observable_events.proto
+++ b/protos/perfetto/common/observable_events.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/sys_stats_counters.proto b/protos/perfetto/common/sys_stats_counters.proto
index 91c4e80..837656b 100644
--- a/protos/perfetto/common/sys_stats_counters.proto
+++ b/protos/perfetto/common/sys_stats_counters.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/trace_stats.proto b/protos/perfetto/common/trace_stats.proto
index 9a661e1..e925ad4 100644
--- a/protos/perfetto/common/trace_stats.proto
+++ b/protos/perfetto/common/trace_stats.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/tracing_service_state.proto b/protos/perfetto/common/tracing_service_state.proto
index 86723b3..dd475ab 100644
--- a/protos/perfetto/common/tracing_service_state.proto
+++ b/protos/perfetto/common/tracing_service_state.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/common/track_event_descriptor.proto b/protos/perfetto/common/track_event_descriptor.proto
index 5536cc8..47d605a 100644
--- a/protos/perfetto/common/track_event_descriptor.proto
+++ b/protos/perfetto/common/track_event_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 1ccbba4..7d40c27 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -45,7 +45,5 @@
 # autogenerated merged proto has a valid syntax.
 perfetto_proto_library("merged_config") {
   proto_generators = [ "lite" ]
-  sources = [
-    "perfetto_config.proto",
-  ]
+  sources = [ "perfetto_config.proto" ]
 }
diff --git a/protos/perfetto/config/android/BUILD.gn b/protos/perfetto/config/android/BUILD.gn
index 6e98fbb..78c2d01 100644
--- a/protos/perfetto/config/android/BUILD.gn
+++ b/protos/perfetto/config/android/BUILD.gn
@@ -16,9 +16,7 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "../../common:@TYPE@",
-  ]
+  deps = [ "../../common:@TYPE@" ]
   sources = [
     "android_log_config.proto",
     "packages_list_config.proto",
diff --git a/protos/perfetto/config/android/android_log_config.proto b/protos/perfetto/config/android/android_log_config.proto
index 5a43f2d..ff4c400 100644
--- a/protos/perfetto/config/android/android_log_config.proto
+++ b/protos/perfetto/config/android/android_log_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 import "protos/perfetto/common/android_log_constants.proto";
diff --git a/protos/perfetto/config/android/packages_list_config.proto b/protos/perfetto/config/android/packages_list_config.proto
index 7796eba..171fc10 100644
--- a/protos/perfetto/config/android/packages_list_config.proto
+++ b/protos/perfetto/config/android/packages_list_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/chrome/BUILD.gn b/protos/perfetto/config/chrome/BUILD.gn
index b893d06..4f44718 100644
--- a/protos/perfetto/config/chrome/BUILD.gn
+++ b/protos/perfetto/config/chrome/BUILD.gn
@@ -16,7 +16,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "chrome_config.proto",
-  ]
+  sources = [ "chrome_config.proto" ]
 }
diff --git a/protos/perfetto/config/chrome/chrome_config.proto b/protos/perfetto/config/chrome/chrome_config.proto
index 3906f03..aa24c38 100644
--- a/protos/perfetto/config/chrome/chrome_config.proto
+++ b/protos/perfetto/config/chrome/chrome_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index e969699..aa071d6 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/ftrace/BUILD.gn b/protos/perfetto/config/ftrace/BUILD.gn
index 37ff9ca..057a0b4 100644
--- a/protos/perfetto/config/ftrace/BUILD.gn
+++ b/protos/perfetto/config/ftrace/BUILD.gn
@@ -16,7 +16,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "ftrace_config.proto",
-  ]
+  sources = [ "ftrace_config.proto" ]
 }
diff --git a/protos/perfetto/config/ftrace/ftrace_config.proto b/protos/perfetto/config/ftrace/ftrace_config.proto
index a612d89..3b78baa 100644
--- a/protos/perfetto/config/ftrace/ftrace_config.proto
+++ b/protos/perfetto/config/ftrace/ftrace_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/gpu/gpu_counter_config.proto b/protos/perfetto/config/gpu/gpu_counter_config.proto
index 017195b..d51a997 100644
--- a/protos/perfetto/config/gpu/gpu_counter_config.proto
+++ b/protos/perfetto/config/gpu/gpu_counter_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/gpu/vulkan_memory_config.proto b/protos/perfetto/config/gpu/vulkan_memory_config.proto
index be86bb2..c1959ae 100644
--- a/protos/perfetto/config/gpu/vulkan_memory_config.proto
+++ b/protos/perfetto/config/gpu/vulkan_memory_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/inode_file/BUILD.gn b/protos/perfetto/config/inode_file/BUILD.gn
index d2cbdfd..4ce2f34 100644
--- a/protos/perfetto/config/inode_file/BUILD.gn
+++ b/protos/perfetto/config/inode_file/BUILD.gn
@@ -15,7 +15,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "inode_file_config.proto",
-  ]
+  sources = [ "inode_file_config.proto" ]
 }
diff --git a/protos/perfetto/config/inode_file/inode_file_config.proto b/protos/perfetto/config/inode_file/inode_file_config.proto
index d291cd1..d94e319 100644
--- a/protos/perfetto/config/inode_file/inode_file_config.proto
+++ b/protos/perfetto/config/inode_file/inode_file_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/power/BUILD.gn b/protos/perfetto/config/power/BUILD.gn
index 0f74515..aa005b5 100644
--- a/protos/perfetto/config/power/BUILD.gn
+++ b/protos/perfetto/config/power/BUILD.gn
@@ -15,7 +15,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "android_power_config.proto",
-  ]
+  sources = [ "android_power_config.proto" ]
 }
diff --git a/protos/perfetto/config/power/android_power_config.proto b/protos/perfetto/config/power/android_power_config.proto
index c17371d..9cc795a 100644
--- a/protos/perfetto/config/power/android_power_config.proto
+++ b/protos/perfetto/config/power/android_power_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/process_stats/BUILD.gn b/protos/perfetto/config/process_stats/BUILD.gn
index 7acfffe..e3aa244 100644
--- a/protos/perfetto/config/process_stats/BUILD.gn
+++ b/protos/perfetto/config/process_stats/BUILD.gn
@@ -15,7 +15,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "process_stats_config.proto",
-  ]
+  sources = [ "process_stats_config.proto" ]
 }
diff --git a/protos/perfetto/config/process_stats/process_stats_config.proto b/protos/perfetto/config/process_stats/process_stats_config.proto
index 14cb4e9..fc406c9 100644
--- a/protos/perfetto/config/process_stats/process_stats_config.proto
+++ b/protos/perfetto/config/process_stats/process_stats_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/profiling/heapprofd_config.proto b/protos/perfetto/config/profiling/heapprofd_config.proto
index 0f3a30f..15a7c68 100644
--- a/protos/perfetto/config/profiling/heapprofd_config.proto
+++ b/protos/perfetto/config/profiling/heapprofd_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/profiling/java_hprof_config.proto b/protos/perfetto/config/profiling/java_hprof_config.proto
index 07d0993..572df33 100644
--- a/protos/perfetto/config/profiling/java_hprof_config.proto
+++ b/protos/perfetto/config/profiling/java_hprof_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/profiling/perf_event_config.proto b/protos/perfetto/config/profiling/perf_event_config.proto
index 7bb967e..0333d71 100644
--- a/protos/perfetto/config/profiling/perf_event_config.proto
+++ b/protos/perfetto/config/profiling/perf_event_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/sys_stats/BUILD.gn b/protos/perfetto/config/sys_stats/BUILD.gn
index 634dd7e..cfe6874 100644
--- a/protos/perfetto/config/sys_stats/BUILD.gn
+++ b/protos/perfetto/config/sys_stats/BUILD.gn
@@ -15,10 +15,6 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "../../common:@TYPE@",
-  ]
-  sources = [
-    "sys_stats_config.proto",
-  ]
+  deps = [ "../../common:@TYPE@" ]
+  sources = [ "sys_stats_config.proto" ]
 }
diff --git a/protos/perfetto/config/sys_stats/sys_stats_config.proto b/protos/perfetto/config/sys_stats/sys_stats_config.proto
index 7eafa3e..4e037fc 100644
--- a/protos/perfetto/config/sys_stats/sys_stats_config.proto
+++ b/protos/perfetto/config/sys_stats/sys_stats_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/test_config.proto b/protos/perfetto/config/test_config.proto
index c22a7e9..40bfa8a 100644
--- a/protos/perfetto/config/test_config.proto
+++ b/protos/perfetto/config/test_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index 46971c1..085f10c 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/config/data_source_config.proto";
 
diff --git a/protos/perfetto/ipc/BUILD.gn b/protos/perfetto/ipc/BUILD.gn
index 383ae5f..3388b7d 100644
--- a/protos/perfetto/ipc/BUILD.gn
+++ b/protos/perfetto/ipc/BUILD.gn
@@ -40,7 +40,5 @@
     "zero",
     "cpp",
   ]
-  sources = [
-    "wire_protocol.proto",
-  ]
+  sources = [ "wire_protocol.proto" ]
 }
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index 26854e8..6a6e3ca 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/common/observable_events.proto";
 import "protos/perfetto/common/tracing_service_state.proto";
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index ebd8b98..fc09783 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/common/commit_data_request.proto";
 import "protos/perfetto/config/data_source_config.proto";
diff --git a/protos/perfetto/ipc/wire_protocol.proto b/protos/perfetto/ipc/wire_protocol.proto
index 5f80f66..c57b71b 100644
--- a/protos/perfetto/ipc/wire_protocol.proto
+++ b/protos/perfetto/ipc/wire_protocol.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/BUILD.gn b/protos/perfetto/metrics/BUILD.gn
index 1bb4387..8a35ef1 100644
--- a/protos/perfetto/metrics/BUILD.gn
+++ b/protos/perfetto/metrics/BUILD.gn
@@ -16,20 +16,14 @@
 import("../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "android:@TYPE@",
-  ]
-  sources = [
-    "metrics.proto",
-  ]
+  deps = [ "android:@TYPE@" ]
+  sources = [ "metrics.proto" ]
 }
 
 if (perfetto_build_standalone) {
   perfetto_proto_library("descriptor") {
     proto_generators = [ "descriptor" ]
     generate_descriptor = "metrics.descriptor"
-    sources = [
-      "metrics.proto",
-    ]
+    sources = [ "metrics.proto" ]
   }
 }
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 5f7e2b4..9b51e5b 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -19,6 +19,7 @@
     "batt_metric.proto",
     "cpu_metric.proto",
     "heap_profile_callsites.proto",
+    "hwui_metric.proto",
     "ion_metric.proto",
     "java_heap_stats.proto",
     "lmk_metric.proto",
diff --git a/protos/perfetto/metrics/android/batt_metric.proto b/protos/perfetto/metrics/android/batt_metric.proto
index b7c2d15..d9d7876 100644
--- a/protos/perfetto/metrics/android/batt_metric.proto
+++ b/protos/perfetto/metrics/android/batt_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/cpu_metric.proto b/protos/perfetto/metrics/android/cpu_metric.proto
index be17af5..12f7588 100644
--- a/protos/perfetto/metrics/android/cpu_metric.proto
+++ b/protos/perfetto/metrics/android/cpu_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/heap_profile_callsites.proto b/protos/perfetto/metrics/android/heap_profile_callsites.proto
index df1a516..af88c6e 100644
--- a/protos/perfetto/metrics/android/heap_profile_callsites.proto
+++ b/protos/perfetto/metrics/android/heap_profile_callsites.proto
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/hwui_metric.proto b/protos/perfetto/metrics/android/hwui_metric.proto
new file mode 100644
index 0000000..b2c11c8
--- /dev/null
+++ b/protos/perfetto/metrics/android/hwui_metric.proto
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+// Android HWUI graphics performance and graphics memory usage metrics.
+message ProcessRenderInfo {
+  // Name of the package launched
+  optional string process_name = 1;
+
+  // CPU time spent on RenderThread in milliseconds.
+  optional int64 rt_cpu_time_ms = 2;
+
+  // Number of frames drawn on RenderThread, followed by max/min/avg CPU time to draw a frame
+  // in nanoseconds.
+  optional uint32 draw_frame_count = 3;
+  optional int64 draw_frame_max = 4;
+  optional int64 draw_frame_min = 5;
+  optional double draw_frame_avg = 6;
+
+  // Number of GPU commands flushes and max/min/avg time per flush in nanoseconds.
+  optional uint32 flush_count = 7;
+  optional int64 flush_max = 8;
+  optional int64 flush_min = 9;
+  optional double flush_avg = 10;
+
+  // Number of View tree preparation counts and max/min/avg time to traverse the tree in
+  // nanoseconds.
+  optional uint32 prepare_tree_count = 11;
+  optional int64 prepare_tree_max = 12;
+  optional int64 prepare_tree_min = 13;
+  optional double prepare_tree_avg = 14;
+
+  // Number of times the GPU rendered a frame and max/min/avg time for GPU to finish rendering in
+  // in nanoseconds.
+  optional uint32 gpu_completion_count = 15;
+  optional int64 gpu_completion_max = 16;
+  optional int64 gpu_completion_min = 17;
+  optional double gpu_completion_avg = 18;
+
+  // Number of times a frame was recorded/serialized in a display list on the UI thread with
+  // max/min/avg time in nanoseconds.
+  optional uint32 ui_record_count = 19;
+  optional int64 ui_record_max = 20;
+  optional int64 ui_record_min = 21;
+  optional double ui_record_avg = 22;
+
+  // number of unique shader programs that were used to render frames, followed by total and average
+  // times to prepare a shader in nanoseconds.
+  optional uint32 shader_compile_count = 23;
+  optional int64 shader_compile_time = 24;
+  optional double shader_compile_avg = 25;
+  // number of shader programs loaded from the disk cache, followed by total time and average time
+  // to prepare a shader in nanoseconds.
+  optional uint32 cache_hit_count = 26;
+  optional int64 cache_hit_time = 27;
+  optional double cache_hit_avg = 28;
+  // number of shader programs compiled/linked, followed by total time and average time to prepare
+  // a shader in nanoseconds.
+  optional uint32 cache_miss_count = 29;
+  optional int64 cache_miss_time = 30;
+  optional double cache_miss_avg = 31;
+
+  // max/min/avg CPU memory used for graphics by HWUI at the end of a frame.
+  optional int64 graphics_cpu_mem_max = 32;
+  optional int64 graphics_cpu_mem_min = 33;
+  optional double graphics_cpu_mem_avg = 34;
+
+  // max/min/avg GPU memory used by HWUI at the end of a frame excluding textures.
+  optional int64 graphics_gpu_mem_max = 35;
+  optional int64 graphics_gpu_mem_min = 36;
+  optional double graphics_gpu_mem_avg = 37;
+
+  // max/min/avg memory used for GPU textures by HWUI at the end of a frame.
+  optional int64 texture_mem_max = 38;
+  optional int64 texture_mem_min = 39;
+  optional double texture_mem_avg = 40;
+
+  // max/min/avg memory used by HWUI at the end of a frame. This is a sum of previous 3 categories.
+  optional int64 all_mem_max = 41;
+  optional int64 all_mem_min = 42;
+  optional double all_mem_avg = 43;
+}
+
+message AndroidHwuiMetric {
+  //  HWUI metrics for processes that have a RenderThread.
+  repeated ProcessRenderInfo process_info = 1;
+}
diff --git a/protos/perfetto/metrics/android/ion_metric.proto b/protos/perfetto/metrics/android/ion_metric.proto
index b50041e..c5a5b19 100644
--- a/protos/perfetto/metrics/android/ion_metric.proto
+++ b/protos/perfetto/metrics/android/ion_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/java_heap_stats.proto b/protos/perfetto/metrics/android/java_heap_stats.proto
index e07190b..85d0595 100644
--- a/protos/perfetto/metrics/android/java_heap_stats.proto
+++ b/protos/perfetto/metrics/android/java_heap_stats.proto
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/lmk_metric.proto b/protos/perfetto/metrics/android/lmk_metric.proto
index 5bb1881..be4218f 100644
--- a/protos/perfetto/metrics/android/lmk_metric.proto
+++ b/protos/perfetto/metrics/android/lmk_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/lmk_reason_metric.proto b/protos/perfetto/metrics/android/lmk_reason_metric.proto
index c21233a..fbfb746 100644
--- a/protos/perfetto/metrics/android/lmk_reason_metric.proto
+++ b/protos/perfetto/metrics/android/lmk_reason_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/mem_metric.proto b/protos/perfetto/metrics/android/mem_metric.proto
index 0443c0d..5fb5c9f 100644
--- a/protos/perfetto/metrics/android/mem_metric.proto
+++ b/protos/perfetto/metrics/android/mem_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/mem_unagg_metric.proto b/protos/perfetto/metrics/android/mem_unagg_metric.proto
index e86893c..fc9276d 100644
--- a/protos/perfetto/metrics/android/mem_unagg_metric.proto
+++ b/protos/perfetto/metrics/android/mem_unagg_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/package_list.proto b/protos/perfetto/metrics/android/package_list.proto
index 3525294..6701979 100644
--- a/protos/perfetto/metrics/android/package_list.proto
+++ b/protos/perfetto/metrics/android/package_list.proto
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/powrails_metric.proto b/protos/perfetto/metrics/android/powrails_metric.proto
index df0823f..fcf0aa7 100644
--- a/protos/perfetto/metrics/android/powrails_metric.proto
+++ b/protos/perfetto/metrics/android/powrails_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/process_metadata.proto b/protos/perfetto/metrics/android/process_metadata.proto
index 792fda0..fd7fe7b 100644
--- a/protos/perfetto/metrics/android/process_metadata.proto
+++ b/protos/perfetto/metrics/android/process_metadata.proto
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index 1310a38..d38a598 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/unmapped_java_symbols.proto b/protos/perfetto/metrics/android/unmapped_java_symbols.proto
index 53266ca..b8c8834 100644
--- a/protos/perfetto/metrics/android/unmapped_java_symbols.proto
+++ b/protos/perfetto/metrics/android/unmapped_java_symbols.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/metrics/android/unsymbolized_frames.proto b/protos/perfetto/metrics/android/unsymbolized_frames.proto
index 98d9d44..da75511 100644
--- a/protos/perfetto/metrics/android/unsymbolized_frames.proto
+++ b/protos/perfetto/metrics/android/unsymbolized_frames.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/test/task_runner_thread_delegates.cc b/protos/perfetto/metrics/custom_options.proto
similarity index 67%
copy from test/task_runner_thread_delegates.cc
copy to protos/perfetto/metrics/custom_options.proto
index 291482f..8f4bfbb 100644
--- a/test/task_runner_thread_delegates.cc
+++ b/protos/perfetto/metrics/custom_options.proto
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include "test/task_runner_thread_delegates.h"
+syntax = "proto2";
 
-namespace perfetto {
+import "google/protobuf/descriptor.proto";
 
-ServiceDelegate::~ServiceDelegate() = default;
-ProbesProducerDelegate::~ProbesProducerDelegate() = default;
-FakeProducerDelegate::~FakeProducerDelegate() = default;
-
-}  // namespace perfetto
+// Field options you can define on a metric. These options are only exposed as
+// part of JSON export.
+extend google.protobuf.FieldOptions {
+  optional string unit = 50001;
+  optional string improvementDirection = 50002;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index c17be13..16be108 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
@@ -29,6 +28,7 @@
 import "protos/perfetto/metrics/android/powrails_metric.proto";
 import "protos/perfetto/metrics/android/startup_metric.proto";
 import "protos/perfetto/metrics/android/heap_profile_callsites.proto";
+import "protos/perfetto/metrics/android/hwui_metric.proto";
 import "protos/perfetto/metrics/android/package_list.proto";
 import "protos/perfetto/metrics/android/unmapped_java_symbols.proto";
 import "protos/perfetto/metrics/android/unsymbolized_frames.proto";
@@ -47,11 +47,12 @@
   optional string trace_uuid = 3;
   optional string android_build_fingerprint = 4;
   optional int64 statsd_triggering_subscription_id = 5;
+  optional int64 trace_size_bytes = 6;
 }
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 20
+// Next id: 21
 message TraceMetrics {
   reserved 4, 10, 13, 14;
 
@@ -103,6 +104,8 @@
   // Java type names that have no deobfuscation mappings.
   optional UnmappedJavaSymbols unmapped_java_symbols = 19;
 
+  optional AndroidHwuiMetric android_hwui_metric = 20;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 3221b5c..4cf3399 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -406,6 +406,94 @@
 
 // End of protos/perfetto/metrics/android/heap_profile_callsites.proto
 
+// Begin of protos/perfetto/metrics/android/hwui_metric.proto
+
+// Android HWUI graphics performance and graphics memory usage metrics.
+message ProcessRenderInfo {
+  // Name of the package launched
+  optional string process_name = 1;
+
+  // CPU time spent on RenderThread in milliseconds.
+  optional int64 rt_cpu_time_ms = 2;
+
+  // Number of frames drawn on RenderThread, followed by max/min/avg CPU time to draw a frame
+  // in nanoseconds.
+  optional uint32 draw_frame_count = 3;
+  optional int64 draw_frame_max = 4;
+  optional int64 draw_frame_min = 5;
+  optional double draw_frame_avg = 6;
+
+  // Number of GPU commands flushes and max/min/avg time per flush in nanoseconds.
+  optional uint32 flush_count = 7;
+  optional int64 flush_max = 8;
+  optional int64 flush_min = 9;
+  optional double flush_avg = 10;
+
+  // Number of View tree preparation counts and max/min/avg time to traverse the tree in
+  // nanoseconds.
+  optional uint32 prepare_tree_count = 11;
+  optional int64 prepare_tree_max = 12;
+  optional int64 prepare_tree_min = 13;
+  optional double prepare_tree_avg = 14;
+
+  // Number of times the GPU rendered a frame and max/min/avg time for GPU to finish rendering in
+  // in nanoseconds.
+  optional uint32 gpu_completion_count = 15;
+  optional int64 gpu_completion_max = 16;
+  optional int64 gpu_completion_min = 17;
+  optional double gpu_completion_avg = 18;
+
+  // Number of times a frame was recorded/serialized in a display list on the UI thread with
+  // max/min/avg time in nanoseconds.
+  optional uint32 ui_record_count = 19;
+  optional int64 ui_record_max = 20;
+  optional int64 ui_record_min = 21;
+  optional double ui_record_avg = 22;
+
+  // number of unique shader programs that were used to render frames, followed by total and average
+  // times to prepare a shader in nanoseconds.
+  optional uint32 shader_compile_count = 23;
+  optional int64 shader_compile_time = 24;
+  optional double shader_compile_avg = 25;
+  // number of shader programs loaded from the disk cache, followed by total time and average time
+  // to prepare a shader in nanoseconds.
+  optional uint32 cache_hit_count = 26;
+  optional int64 cache_hit_time = 27;
+  optional double cache_hit_avg = 28;
+  // number of shader programs compiled/linked, followed by total time and average time to prepare
+  // a shader in nanoseconds.
+  optional uint32 cache_miss_count = 29;
+  optional int64 cache_miss_time = 30;
+  optional double cache_miss_avg = 31;
+
+  // max/min/avg CPU memory used for graphics by HWUI at the end of a frame.
+  optional int64 graphics_cpu_mem_max = 32;
+  optional int64 graphics_cpu_mem_min = 33;
+  optional double graphics_cpu_mem_avg = 34;
+
+  // max/min/avg GPU memory used by HWUI at the end of a frame excluding textures.
+  optional int64 graphics_gpu_mem_max = 35;
+  optional int64 graphics_gpu_mem_min = 36;
+  optional double graphics_gpu_mem_avg = 37;
+
+  // max/min/avg memory used for GPU textures by HWUI at the end of a frame.
+  optional int64 texture_mem_max = 38;
+  optional int64 texture_mem_min = 39;
+  optional double texture_mem_avg = 40;
+
+  // max/min/avg memory used by HWUI at the end of a frame. This is a sum of previous 3 categories.
+  optional int64 all_mem_max = 41;
+  optional int64 all_mem_min = 42;
+  optional double all_mem_avg = 43;
+}
+
+message AndroidHwuiMetric {
+  //  HWUI metrics for processes that have a RenderThread.
+  repeated ProcessRenderInfo process_info = 1;
+}
+
+// End of protos/perfetto/metrics/android/hwui_metric.proto
+
 // Begin of protos/perfetto/metrics/android/package_list.proto
 
 message AndroidPackageList {
@@ -485,11 +573,12 @@
   optional string trace_uuid = 3;
   optional string android_build_fingerprint = 4;
   optional int64 statsd_triggering_subscription_id = 5;
+  optional int64 trace_size_bytes = 6;
 }
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 20
+// Next id: 21
 message TraceMetrics {
   reserved 4, 10, 13, 14;
 
@@ -541,6 +630,8 @@
   // Java type names that have no deobfuscation mappings.
   optional UnmappedJavaSymbols unmapped_java_symbols = 19;
 
+  optional AndroidHwuiMetric android_hwui_metric = 20;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index ebcdfde..9f27411 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -93,9 +93,7 @@
 }
 
 perfetto_proto_library("minimal_@TYPE@") {
-  deps = [
-    "../config:@TYPE@",
-  ]
+  deps = [ "../config:@TYPE@" ]
   sources = proto_sources_minimal
 }
 
@@ -103,18 +101,14 @@
 # autogenerated merged proto has a valid syntax.
 perfetto_proto_library("merged_trace") {
   proto_generators = [ "lite" ]
-  sources = [
-    "perfetto_trace.proto",
-  ]
+  sources = [ "perfetto_trace.proto" ]
 }
 
 if (perfetto_build_standalone) {
   perfetto_proto_library("descriptor") {
     proto_generators = [ "descriptor" ]
     generate_descriptor = "trace.descriptor"
-    sources = [
-      "trace.proto",
-    ]
+    sources = [ "trace.proto" ]
   }
 }
 
@@ -122,7 +116,5 @@
 # targets to implement custom parsers based on our protos.
 static_library("perfetto_trace_protos") {
   complete_static_lib = true
-  deps = [
-    ":lite",
-  ]
+  deps = [ ":lite" ]
 }
diff --git a/protos/perfetto/trace/android/BUILD.gn b/protos/perfetto/trace/android/BUILD.gn
index 3a8334b..98163d6 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -15,9 +15,7 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "../../common:@TYPE@",
-  ]
+  deps = [ "../../common:@TYPE@" ]
 
   sources = [
     "android_log.proto",
diff --git a/protos/perfetto/trace/android/android_log.proto b/protos/perfetto/trace/android/android_log.proto
index 41edacc..4ee0a5d 100644
--- a/protos/perfetto/trace/android/android_log.proto
+++ b/protos/perfetto/trace/android/android_log.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 import "protos/perfetto/common/android_log_constants.proto";
diff --git a/protos/perfetto/trace/android/graphics_frame_event.proto b/protos/perfetto/trace/android/graphics_frame_event.proto
index 36cfa39..616aca8 100644
--- a/protos/perfetto/trace/android/graphics_frame_event.proto
+++ b/protos/perfetto/trace/android/graphics_frame_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 // Generated by Android's SurfaceFlinger.
diff --git a/protos/perfetto/trace/android/packages_list.proto b/protos/perfetto/trace/android/packages_list.proto
index 8ae6200..1c4100f 100644
--- a/protos/perfetto/trace/android/packages_list.proto
+++ b/protos/perfetto/trace/android/packages_list.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message PackagesList {
diff --git a/protos/perfetto/trace/chrome/BUILD.gn b/protos/perfetto/trace/chrome/BUILD.gn
index e49afe9..dc4ecb2 100644
--- a/protos/perfetto/trace/chrome/BUILD.gn
+++ b/protos/perfetto/trace/chrome/BUILD.gn
@@ -33,7 +33,5 @@
     "../profiling:@TYPE@",
     "../track_event:@TYPE@",
   ]
-  sources = [
-    "chrome_trace_packet.proto",
-  ]
+  sources = [ "chrome_trace_packet.proto" ]
 }
diff --git a/protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto b/protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto
index 1500660..af26892 100644
--- a/protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto
+++ b/protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 // This message is not intended to be written by the chrome on the device.
diff --git a/protos/perfetto/trace/chrome/chrome_metadata.proto b/protos/perfetto/trace/chrome/chrome_metadata.proto
index 9e91c8f..e08aebb 100644
--- a/protos/perfetto/trace/chrome/chrome_metadata.proto
+++ b/protos/perfetto/trace/chrome/chrome_metadata.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/chrome/chrome_trace_event.proto b/protos/perfetto/trace/chrome/chrome_trace_event.proto
index 62c0ad3..1c3725c 100644
--- a/protos/perfetto/trace/chrome/chrome_trace_event.proto
+++ b/protos/perfetto/trace/chrome/chrome_trace_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message ChromeTracedValue {
diff --git a/protos/perfetto/trace/chrome/chrome_trace_packet.proto b/protos/perfetto/trace/chrome/chrome_trace_packet.proto
index 956f07d..ae0201d 100644
--- a/protos/perfetto/trace/chrome/chrome_trace_packet.proto
+++ b/protos/perfetto/trace/chrome/chrome_trace_packet.proto
@@ -24,7 +24,6 @@
 // https://android-review.googlesource.com/c/platform/external/perfetto/+/
 // 591673#17 for details.
 syntax = "proto3";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/common/trace_stats.proto";
 import "protos/perfetto/config/trace_config.proto";
diff --git a/protos/perfetto/trace/clock_snapshot.proto b/protos/perfetto/trace/clock_snapshot.proto
index e3dff0f..16c6ce5 100644
--- a/protos/perfetto/trace/clock_snapshot.proto
+++ b/protos/perfetto/trace/clock_snapshot.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/filesystem/BUILD.gn b/protos/perfetto/trace/filesystem/BUILD.gn
index 7d415e1..96d7d65 100644
--- a/protos/perfetto/trace/filesystem/BUILD.gn
+++ b/protos/perfetto/trace/filesystem/BUILD.gn
@@ -15,7 +15,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "inode_file_map.proto",
-  ]
+  sources = [ "inode_file_map.proto" ]
 }
diff --git a/protos/perfetto/trace/filesystem/inode_file_map.proto b/protos/perfetto/trace/filesystem/inode_file_map.proto
index 92f8581..e1d4c98 100644
--- a/protos/perfetto/trace/filesystem/inode_file_map.proto
+++ b/protos/perfetto/trace/filesystem/inode_file_map.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 // Represents the mapping between inode numbers in a block device and their path
diff --git a/protos/perfetto/trace/ftrace/BUILD.gn b/protos/perfetto/trace/ftrace/BUILD.gn
index 3fa8cc0..5684450 100644
--- a/protos/perfetto/trace/ftrace/BUILD.gn
+++ b/protos/perfetto/trace/ftrace/BUILD.gn
@@ -29,8 +29,6 @@
   perfetto_proto_library("descriptor") {
     proto_generators = [ "descriptor" ]
     generate_descriptor = "ftrace.descriptor"
-    sources = [
-      "ftrace_event_bundle.proto",
-    ]
+    sources = [ "ftrace_event_bundle.proto" ]
   }
 }
diff --git a/protos/perfetto/trace/ftrace/binder.proto b/protos/perfetto/trace/ftrace/binder.proto
index 89187e6..ecec4a1 100644
--- a/protos/perfetto/trace/ftrace/binder.proto
+++ b/protos/perfetto/trace/ftrace/binder.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message BinderTransactionFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/block.proto b/protos/perfetto/trace/ftrace/block.proto
index 27d6a69..c39a7a2 100644
--- a/protos/perfetto/trace/ftrace/block.proto
+++ b/protos/perfetto/trace/ftrace/block.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message BlockRqIssueFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/cgroup.proto b/protos/perfetto/trace/ftrace/cgroup.proto
index 0e52c1e..b8a7d1e 100644
--- a/protos/perfetto/trace/ftrace/cgroup.proto
+++ b/protos/perfetto/trace/ftrace/cgroup.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message CgroupAttachTaskFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/clk.proto b/protos/perfetto/trace/ftrace/clk.proto
index ae6470d..6d5b753 100644
--- a/protos/perfetto/trace/ftrace/clk.proto
+++ b/protos/perfetto/trace/ftrace/clk.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message ClkEnableFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/compaction.proto b/protos/perfetto/trace/ftrace/compaction.proto
index 2d24a48..cb57262 100644
--- a/protos/perfetto/trace/ftrace/compaction.proto
+++ b/protos/perfetto/trace/ftrace/compaction.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message MmCompactionBeginFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/ext4.proto b/protos/perfetto/trace/ftrace/ext4.proto
index 4c7d7a0..90d0322 100644
--- a/protos/perfetto/trace/ftrace/ext4.proto
+++ b/protos/perfetto/trace/ftrace/ext4.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message Ext4DaWriteBeginFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/f2fs.proto b/protos/perfetto/trace/ftrace/f2fs.proto
index 2526b92..556c964 100644
--- a/protos/perfetto/trace/ftrace/f2fs.proto
+++ b/protos/perfetto/trace/ftrace/f2fs.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message F2fsDoSubmitBioFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/fence.proto b/protos/perfetto/trace/ftrace/fence.proto
index a03fb1a..141a509 100644
--- a/protos/perfetto/trace/ftrace/fence.proto
+++ b/protos/perfetto/trace/ftrace/fence.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message FenceInitFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/filemap.proto b/protos/perfetto/trace/ftrace/filemap.proto
index 82d892f..3d70c1f 100644
--- a/protos/perfetto/trace/ftrace/filemap.proto
+++ b/protos/perfetto/trace/ftrace/filemap.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message MmFilemapAddToPageCacheFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/ftrace.proto b/protos/perfetto/trace/ftrace/ftrace.proto
index 86b7a6d..9fc6f56 100644
--- a/protos/perfetto/trace/ftrace/ftrace.proto
+++ b/protos/perfetto/trace/ftrace/ftrace.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message PrintFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index d6dfc74..38e4adc 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -19,7 +19,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/ftrace/binder.proto";
 import "protos/perfetto/trace/ftrace/block.proto";
diff --git a/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto b/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
index 1d69b1f..69aedaa 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/ftrace/ftrace_event.proto";
 
diff --git a/protos/perfetto/trace/ftrace/ftrace_stats.proto b/protos/perfetto/trace/ftrace/ftrace_stats.proto
index 53fc392..5e6ea92 100644
--- a/protos/perfetto/trace/ftrace/ftrace_stats.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_stats.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/ftrace/generic.proto b/protos/perfetto/trace/ftrace/generic.proto
index 54aec3d..65d5c95 100644
--- a/protos/perfetto/trace/ftrace/generic.proto
+++ b/protos/perfetto/trace/ftrace/generic.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/ftrace/i2c.proto b/protos/perfetto/trace/ftrace/i2c.proto
index e35f17c..8fffd40 100644
--- a/protos/perfetto/trace/ftrace/i2c.proto
+++ b/protos/perfetto/trace/ftrace/i2c.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message I2cReadFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/ipi.proto b/protos/perfetto/trace/ftrace/ipi.proto
index e267bff..73aeebe 100644
--- a/protos/perfetto/trace/ftrace/ipi.proto
+++ b/protos/perfetto/trace/ftrace/ipi.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message IpiEntryFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/irq.proto b/protos/perfetto/trace/ftrace/irq.proto
index f5d532b..8e9c786 100644
--- a/protos/perfetto/trace/ftrace/irq.proto
+++ b/protos/perfetto/trace/ftrace/irq.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message SoftirqEntryFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/kfree.proto b/protos/perfetto/trace/ftrace/kfree.proto
index 2597408..3a5ce00 100644
--- a/protos/perfetto/trace/ftrace/kfree.proto
+++ b/protos/perfetto/trace/ftrace/kfree.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message KfreeFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/kmalloc.proto b/protos/perfetto/trace/ftrace/kmalloc.proto
index d4b4f91..81d7d99 100644
--- a/protos/perfetto/trace/ftrace/kmalloc.proto
+++ b/protos/perfetto/trace/ftrace/kmalloc.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message KmallocFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/kmem.proto b/protos/perfetto/trace/ftrace/kmem.proto
index b207d2b..e9c52de 100644
--- a/protos/perfetto/trace/ftrace/kmem.proto
+++ b/protos/perfetto/trace/ftrace/kmem.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message AllocPagesIommuEndFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/lowmemorykiller.proto b/protos/perfetto/trace/ftrace/lowmemorykiller.proto
index ea1ce0d..67c7f5e 100644
--- a/protos/perfetto/trace/ftrace/lowmemorykiller.proto
+++ b/protos/perfetto/trace/ftrace/lowmemorykiller.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message LowmemoryKillFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/mdss.proto b/protos/perfetto/trace/ftrace/mdss.proto
index 9345111..d7b3c42 100644
--- a/protos/perfetto/trace/ftrace/mdss.proto
+++ b/protos/perfetto/trace/ftrace/mdss.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message MdpCmdKickoffFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/mm_event.proto b/protos/perfetto/trace/ftrace/mm_event.proto
index 063e214..59b91db 100644
--- a/protos/perfetto/trace/ftrace/mm_event.proto
+++ b/protos/perfetto/trace/ftrace/mm_event.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message MmEventRecordFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/oom.proto b/protos/perfetto/trace/ftrace/oom.proto
index da46153..015f2da 100644
--- a/protos/perfetto/trace/ftrace/oom.proto
+++ b/protos/perfetto/trace/ftrace/oom.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message OomScoreAdjUpdateFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/power.proto b/protos/perfetto/trace/ftrace/power.proto
index a79cc03..f38015a 100644
--- a/protos/perfetto/trace/ftrace/power.proto
+++ b/protos/perfetto/trace/ftrace/power.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message CpuFrequencyFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/print.proto b/protos/perfetto/trace/ftrace/print.proto
index 86b7a6d..9fc6f56 100644
--- a/protos/perfetto/trace/ftrace/print.proto
+++ b/protos/perfetto/trace/ftrace/print.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message PrintFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/raw_syscalls.proto b/protos/perfetto/trace/ftrace/raw_syscalls.proto
index 0dda679..405202b 100644
--- a/protos/perfetto/trace/ftrace/raw_syscalls.proto
+++ b/protos/perfetto/trace/ftrace/raw_syscalls.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message SysEnterFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/regulator.proto b/protos/perfetto/trace/ftrace/regulator.proto
index 97a42ef..1776b05 100644
--- a/protos/perfetto/trace/ftrace/regulator.proto
+++ b/protos/perfetto/trace/ftrace/regulator.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message RegulatorDisableFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/sched.proto b/protos/perfetto/trace/ftrace/sched.proto
index 4d546b4..9c31fac 100644
--- a/protos/perfetto/trace/ftrace/sched.proto
+++ b/protos/perfetto/trace/ftrace/sched.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message SchedSwitchFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/sde.proto b/protos/perfetto/trace/ftrace/sde.proto
index fbafd2e..724a9a3 100644
--- a/protos/perfetto/trace/ftrace/sde.proto
+++ b/protos/perfetto/trace/ftrace/sde.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message SdeTracingMarkWriteFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/signal.proto b/protos/perfetto/trace/ftrace/signal.proto
index 41b26bd..6e76d0e 100644
--- a/protos/perfetto/trace/ftrace/signal.proto
+++ b/protos/perfetto/trace/ftrace/signal.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message SignalDeliverFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/sync.proto b/protos/perfetto/trace/ftrace/sync.proto
index cf5a6d8..0105a9d 100644
--- a/protos/perfetto/trace/ftrace/sync.proto
+++ b/protos/perfetto/trace/ftrace/sync.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message SyncPtFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/systrace.proto b/protos/perfetto/trace/ftrace/systrace.proto
index b52b305..5296125 100644
--- a/protos/perfetto/trace/ftrace/systrace.proto
+++ b/protos/perfetto/trace/ftrace/systrace.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message ZeroFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/task.proto b/protos/perfetto/trace/ftrace/task.proto
index 4e966aa..878e04f 100644
--- a/protos/perfetto/trace/ftrace/task.proto
+++ b/protos/perfetto/trace/ftrace/task.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message TaskNewtaskFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/test_bundle_wrapper.proto b/protos/perfetto/trace/ftrace/test_bundle_wrapper.proto
index c15c3f6..72c1344 100644
--- a/protos/perfetto/trace/ftrace/test_bundle_wrapper.proto
+++ b/protos/perfetto/trace/ftrace/test_bundle_wrapper.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 import "protos/perfetto/trace/ftrace/ftrace_event_bundle.proto";
 
diff --git a/protos/perfetto/trace/ftrace/vmscan.proto b/protos/perfetto/trace/ftrace/vmscan.proto
index 242acff..f5c5f66 100644
--- a/protos/perfetto/trace/ftrace/vmscan.proto
+++ b/protos/perfetto/trace/ftrace/vmscan.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message MmVmscanDirectReclaimBeginFtraceEvent {
diff --git a/protos/perfetto/trace/ftrace/workqueue.proto b/protos/perfetto/trace/ftrace/workqueue.proto
index 23cc342..dd68361 100644
--- a/protos/perfetto/trace/ftrace/workqueue.proto
+++ b/protos/perfetto/trace/ftrace/workqueue.proto
@@ -3,7 +3,6 @@
 // Do not edit.
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message WorkqueueActivateWorkFtraceEvent {
diff --git a/protos/perfetto/trace/gpu/BUILD.gn b/protos/perfetto/trace/gpu/BUILD.gn
index fd1ba9f..6caf06c 100644
--- a/protos/perfetto/trace/gpu/BUILD.gn
+++ b/protos/perfetto/trace/gpu/BUILD.gn
@@ -15,9 +15,7 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "../../common:@TYPE@",
-  ]
+  deps = [ "../../common:@TYPE@" ]
   sources = [
     "gpu_counter_event.proto",
     "gpu_log.proto",
diff --git a/protos/perfetto/trace/gpu/gpu_counter_event.proto b/protos/perfetto/trace/gpu/gpu_counter_event.proto
index fe3a8f0..ee109b2 100644
--- a/protos/perfetto/trace/gpu/gpu_counter_event.proto
+++ b/protos/perfetto/trace/gpu/gpu_counter_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/gpu/gpu_log.proto b/protos/perfetto/trace/gpu/gpu_log.proto
index 1c39bc0..3c10e36 100644
--- a/protos/perfetto/trace/gpu/gpu_log.proto
+++ b/protos/perfetto/trace/gpu/gpu_log.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/gpu/gpu_render_stage_event.proto b/protos/perfetto/trace/gpu/gpu_render_stage_event.proto
index 6e62a4b..16f8b00 100644
--- a/protos/perfetto/trace/gpu/gpu_render_stage_event.proto
+++ b/protos/perfetto/trace/gpu/gpu_render_stage_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/gpu/vulkan_api_event.proto b/protos/perfetto/trace/gpu/vulkan_api_event.proto
index 5b7ef04..08d9947 100644
--- a/protos/perfetto/trace/gpu/vulkan_api_event.proto
+++ b/protos/perfetto/trace/gpu/vulkan_api_event.proto
@@ -15,18 +15,20 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
 // Message for recording the Vulkan call.
 message VulkanApiEvent {
-  oneof event { VkDebugUtilsObjectName vk_debug_utils_object_name = 1; }
+  oneof event {
+    VkDebugUtilsObjectName vk_debug_utils_object_name = 1;
+    VkQueueSubmit vk_queue_submit = 2;
+  }
 
   // For recording vkSetDebugUtilsObjectNameEXT and
   // vkDebugMarkerSetObjectNameEXT
   message VkDebugUtilsObjectName {
-    optional int32 pid = 1;
+    optional uint32 pid = 1;
     optional uint64 vk_device = 2;
     // VkObjectType.  Value must match
     // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkObjectType.html.
@@ -34,4 +36,17 @@
     optional uint64 object = 4;
     optional string object_name = 5;
   }
+
+  // For recording vkQueueSubmit call.
+  message VkQueueSubmit {
+    optional uint64 duration_ns = 1;
+    optional uint32 pid = 2;
+    optional uint32 tid = 3;
+    optional uint64 vk_queue = 4;
+    repeated uint64 vk_command_buffers = 5;
+    // Submission ID.  An identifier unique to each vkQueueSubmit call.  This
+    // submission_id must match GpuRenderStageEvent.submission_id if the
+    // GpuRenderStageEvent is created due to this vkQueueSubmit.
+    optional uint32 submission_id = 6;
+  }
 }
diff --git a/protos/perfetto/trace/gpu/vulkan_memory_event.proto b/protos/perfetto/trace/gpu/vulkan_memory_event.proto
index d37bacb..a8963cf 100644
--- a/protos/perfetto/trace/gpu/vulkan_memory_event.proto
+++ b/protos/perfetto/trace/gpu/vulkan_memory_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/interned_data/BUILD.gn b/protos/perfetto/trace/interned_data/BUILD.gn
index dbda3ab..9ca2273 100644
--- a/protos/perfetto/trace/interned_data/BUILD.gn
+++ b/protos/perfetto/trace/interned_data/BUILD.gn
@@ -15,9 +15,7 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "interned_data.proto",
-  ]
+  sources = [ "interned_data.proto" ]
   deps = [
     "../profiling:@TYPE@",
     "../track_event:@TYPE@",
diff --git a/protos/perfetto/trace/interned_data/interned_data.proto b/protos/perfetto/trace/interned_data/interned_data.proto
index 3cacef9..8b80554 100644
--- a/protos/perfetto/trace/interned_data/interned_data.proto
+++ b/protos/perfetto/trace/interned_data/interned_data.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/track_event/debug_annotation.proto";
 import "protos/perfetto/trace/track_event/log_message.proto";
diff --git a/protos/perfetto/trace/perfetto/BUILD.gn b/protos/perfetto/trace/perfetto/BUILD.gn
index 5fc30a4..6e69286 100644
--- a/protos/perfetto/trace/perfetto/BUILD.gn
+++ b/protos/perfetto/trace/perfetto/BUILD.gn
@@ -15,7 +15,5 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  sources = [
-    "perfetto_metatrace.proto",
-  ]
+  sources = [ "perfetto_metatrace.proto" ]
 }
diff --git a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
index 63ceacb..f096101 100644
--- a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+++ b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index d7ee5c6..d70c75e 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -4080,7 +4080,7 @@
 // Next id: 4.
 message ChromeProcessDescriptor {
   // See chromium's content::ProcessType.
-  enum ChromeProcessType {
+  enum ProcessType {
     PROCESS_UNSPECIFIED = 0;
     PROCESS_BROWSER = 1;
     PROCESS_RENDERER = 2;
@@ -4091,7 +4091,7 @@
     PROCESS_PPAPI_PLUGIN = 7;
     PROCESS_PPAPI_BROKER = 8;
   }
-  optional ChromeProcessType process_type = 1;
+  optional ProcessType process_type = 1;
   optional int32 process_priority = 2;
 
   // To support old UI. New UI should determine default sorting by process_type.
@@ -4117,8 +4117,8 @@
     // Scheduler:
     THREAD_POOL_BG_WORKER = 3;
     THREAD_POOL_FG_WORKER = 4;
-    THREAD_POOL_FB_BLOCKING = 5;
     THREAD_POOL_BG_BLOCKING = 6;
+    THREAD_POOL_FG_BLOCKING = 5;
     THREAD_POOL_SERVICE = 7;
 
     // Compositor:
@@ -4237,10 +4237,11 @@
 // Describes a process's attributes. Emitted as part of a TrackDescriptor,
 // usually by the process's main thread.
 //
-// Next id: 6.
+// Next id: 7.
 message ProcessDescriptor {
   optional int32 pid = 1;
   repeated string cmdline = 2;
+  optional string process_name = 6;
 
   optional int32 process_priority = 5;
 
@@ -4357,9 +4358,7 @@
 // Begin of protos/perfetto/trace/track_event/track_descriptor.proto
 
 // Defines a track for TrackEvents. Slices and instant events on the same track
-// will be nested based on their timestamps, see TrackEvent::Type. Additionally
-// events on tracks which represent the same thread (i.e., matching pid and tid
-// in ThreadDescriptor) will be merged onto one sequential timeline.
+// will be nested based on their timestamps, see TrackEvent::Type.
 //
 // A TrackDescriptor only needs to be emitted by one trace writer / producer and
 // is valid for the entirety of the trace. To ensure the descriptor isn't lost
@@ -4389,10 +4388,24 @@
   // Name of the track.
   optional string name = 2;
 
-  // Associate the track with a process or thread - the UI will merge all tracks
-  // for the same process / thread into a single timeline view.
+  // Associate the track with a process, making it the process-global track.
+  // There should only be one such track per process (usually for instant
+  // events; trace processor uses this fact to detect pid reuse). If you need
+  // more (e.g. for asynchronous events), create child tracks using parent_uuid.
+  //
+  // Trace processor will merge events on a process track with slice-type events
+  // from other sources (e.g. ftrace) for the same process into a single
+  // timeline view.
   optional ProcessDescriptor process = 3;
   optional ChromeProcessDescriptor chrome_process = 6;
+
+  // Associate the track with a thread, indicating that the track's events
+  // describe synchronous code execution on the thread. There should only be one
+  // such track per thread (trace processor uses this fact to detect tid reuse).
+  //
+  // Trace processor will merge events on a thread track with slice-type events
+  // from other sources (e.g. ftrace) for the same thread into a single timeline
+  // view.
   optional ThreadDescriptor thread = 4;
   optional ChromeThreadDescriptor chrome_thread = 7;
 }
@@ -4812,12 +4825,15 @@
 
 // Message for recording the Vulkan call.
 message VulkanApiEvent {
-  oneof event { VkDebugUtilsObjectName vk_debug_utils_object_name = 1; }
+  oneof event {
+    VkDebugUtilsObjectName vk_debug_utils_object_name = 1;
+    VkQueueSubmit vk_queue_submit = 2;
+  }
 
   // For recording vkSetDebugUtilsObjectNameEXT and
   // vkDebugMarkerSetObjectNameEXT
   message VkDebugUtilsObjectName {
-    optional int32 pid = 1;
+    optional uint32 pid = 1;
     optional uint64 vk_device = 2;
     // VkObjectType.  Value must match
     // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkObjectType.html.
@@ -4825,6 +4841,19 @@
     optional uint64 object = 4;
     optional string object_name = 5;
   }
+
+  // For recording vkQueueSubmit call.
+  message VkQueueSubmit {
+    optional uint64 duration_ns = 1;
+    optional uint32 pid = 2;
+    optional uint32 tid = 3;
+    optional uint64 vk_queue = 4;
+    repeated uint64 vk_command_buffers = 5;
+    // Submission ID.  An identifier unique to each vkQueueSubmit call.  This
+    // submission_id must match GpuRenderStageEvent.submission_id if the
+    // GpuRenderStageEvent is created due to this vkQueueSubmit.
+    optional uint32 submission_id = 6;
+  }
 }
 
 // End of protos/perfetto/trace/gpu/vulkan_api_event.proto
diff --git a/protos/perfetto/trace/power/battery_counters.proto b/protos/perfetto/trace/power/battery_counters.proto
index 383ecf8..2337c41 100644
--- a/protos/perfetto/trace/power/battery_counters.proto
+++ b/protos/perfetto/trace/power/battery_counters.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message BatteryCounters {
diff --git a/protos/perfetto/trace/power/power_rails.proto b/protos/perfetto/trace/power/power_rails.proto
index d4a718c..efe8a52 100644
--- a/protos/perfetto/trace/power/power_rails.proto
+++ b/protos/perfetto/trace/power/power_rails.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message PowerRails {
diff --git a/protos/perfetto/trace/profiling/heap_graph.proto b/protos/perfetto/trace/profiling/heap_graph.proto
index 933d2d0..134168c 100644
--- a/protos/perfetto/trace/profiling/heap_graph.proto
+++ b/protos/perfetto/trace/profiling/heap_graph.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/profiling/profile_common.proto";
 
diff --git a/protos/perfetto/trace/profiling/profile_common.proto b/protos/perfetto/trace/profiling/profile_common.proto
index 9b88de2..1c0bd6f 100644
--- a/protos/perfetto/trace/profiling/profile_common.proto
+++ b/protos/perfetto/trace/profiling/profile_common.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/profiling/profile_packet.proto b/protos/perfetto/trace/profiling/profile_packet.proto
index 300e2b7..845f137 100644
--- a/protos/perfetto/trace/profiling/profile_packet.proto
+++ b/protos/perfetto/trace/profiling/profile_packet.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/profiling/profile_common.proto";
 
diff --git a/protos/perfetto/trace/ps/process_stats.proto b/protos/perfetto/trace/ps/process_stats.proto
index 5db028f..66b133d 100644
--- a/protos/perfetto/trace/ps/process_stats.proto
+++ b/protos/perfetto/trace/ps/process_stats.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 // Per-process periodically sampled stats. These samples are wrapped in a
diff --git a/protos/perfetto/trace/ps/process_tree.proto b/protos/perfetto/trace/ps/process_tree.proto
index 14d3b6d..446c689 100644
--- a/protos/perfetto/trace/ps/process_tree.proto
+++ b/protos/perfetto/trace/ps/process_tree.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 message ProcessTree {
diff --git a/protos/perfetto/trace/sys_stats/BUILD.gn b/protos/perfetto/trace/sys_stats/BUILD.gn
index aa79c47..594b7e7 100644
--- a/protos/perfetto/trace/sys_stats/BUILD.gn
+++ b/protos/perfetto/trace/sys_stats/BUILD.gn
@@ -15,10 +15,6 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "../../common:@TYPE@",
-  ]
-  sources = [
-    "sys_stats.proto",
-  ]
+  deps = [ "../../common:@TYPE@" ]
+  sources = [ "sys_stats.proto" ]
 }
diff --git a/protos/perfetto/trace/sys_stats/sys_stats.proto b/protos/perfetto/trace/sys_stats/sys_stats.proto
index ad7db64..ce485f3 100644
--- a/protos/perfetto/trace/sys_stats/sys_stats.proto
+++ b/protos/perfetto/trace/sys_stats/sys_stats.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 import "protos/perfetto/common/sys_stats_counters.proto";
diff --git a/protos/perfetto/trace/system_info.proto b/protos/perfetto/trace/system_info.proto
index 434061e..4d2fbaa 100644
--- a/protos/perfetto/trace/system_info.proto
+++ b/protos/perfetto/trace/system_info.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/test_event.proto b/protos/perfetto/trace/test_event.proto
index 769b6b0..58a4537 100644
--- a/protos/perfetto/trace/test_event.proto
+++ b/protos/perfetto/trace/test_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/trace.proto b/protos/perfetto/trace/trace.proto
index fa8677e..2a1cd79 100644
--- a/protos/perfetto/trace/trace.proto
+++ b/protos/perfetto/trace/trace.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/trace_packet.proto";
 
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index da3b6af..bc3c9bf 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/common/trace_stats.proto";
 import "protos/perfetto/config/trace_config.proto";
diff --git a/protos/perfetto/trace/trace_packet_defaults.proto b/protos/perfetto/trace/trace_packet_defaults.proto
index b6f3e83..032b852 100644
--- a/protos/perfetto/trace/trace_packet_defaults.proto
+++ b/protos/perfetto/trace/trace_packet_defaults.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/track_event/track_event.proto";
 
diff --git a/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto b/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto
index 40853d7..109180a 100644
--- a/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto
+++ b/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/track_event/source_location.proto";
 
diff --git a/protos/perfetto/trace/track_event/chrome_histogram_sample.proto b/protos/perfetto/trace/track_event/chrome_histogram_sample.proto
index 902a9dcc..67dd28c 100644
--- a/protos/perfetto/trace/track_event/chrome_histogram_sample.proto
+++ b/protos/perfetto/trace/track_event/chrome_histogram_sample.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/chrome_keyed_service.proto b/protos/perfetto/trace/track_event/chrome_keyed_service.proto
index 95ce21d..1cb3e09 100644
--- a/protos/perfetto/trace/track_event/chrome_keyed_service.proto
+++ b/protos/perfetto/trace/track_event/chrome_keyed_service.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/chrome_legacy_ipc.proto b/protos/perfetto/trace/track_event/chrome_legacy_ipc.proto
index df6dcce..1b03889 100644
--- a/protos/perfetto/trace/track_event/chrome_legacy_ipc.proto
+++ b/protos/perfetto/trace/track_event/chrome_legacy_ipc.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/chrome_process_descriptor.proto b/protos/perfetto/trace/track_event/chrome_process_descriptor.proto
index 25e5d6f..f7d0177 100644
--- a/protos/perfetto/trace/track_event/chrome_process_descriptor.proto
+++ b/protos/perfetto/trace/track_event/chrome_process_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
@@ -25,7 +24,7 @@
 // Next id: 4.
 message ChromeProcessDescriptor {
   // See chromium's content::ProcessType.
-  enum ChromeProcessType {
+  enum ProcessType {
     PROCESS_UNSPECIFIED = 0;
     PROCESS_BROWSER = 1;
     PROCESS_RENDERER = 2;
@@ -36,7 +35,7 @@
     PROCESS_PPAPI_PLUGIN = 7;
     PROCESS_PPAPI_BROKER = 8;
   }
-  optional ChromeProcessType process_type = 1;
+  optional ProcessType process_type = 1;
   optional int32 process_priority = 2;
 
   // To support old UI. New UI should determine default sorting by process_type.
diff --git a/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto b/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
index 0f23e7b..d19e3bd 100644
--- a/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
+++ b/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
@@ -34,8 +33,8 @@
     // Scheduler:
     THREAD_POOL_BG_WORKER = 3;
     THREAD_POOL_FG_WORKER = 4;
-    THREAD_POOL_FB_BLOCKING = 5;
     THREAD_POOL_BG_BLOCKING = 6;
+    THREAD_POOL_FG_BLOCKING = 5;
     THREAD_POOL_SERVICE = 7;
 
     // Compositor:
diff --git a/protos/perfetto/trace/track_event/chrome_user_event.proto b/protos/perfetto/trace/track_event/chrome_user_event.proto
index 4f6d869..d071830 100644
--- a/protos/perfetto/trace/track_event/chrome_user_event.proto
+++ b/protos/perfetto/trace/track_event/chrome_user_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/debug_annotation.proto b/protos/perfetto/trace/track_event/debug_annotation.proto
index 1bbfaa9..8b755d8 100644
--- a/protos/perfetto/trace/track_event/debug_annotation.proto
+++ b/protos/perfetto/trace/track_event/debug_annotation.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/log_message.proto b/protos/perfetto/trace/track_event/log_message.proto
index a2fde6d..55263f9 100644
--- a/protos/perfetto/trace/track_event/log_message.proto
+++ b/protos/perfetto/trace/track_event/log_message.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/process_descriptor.proto b/protos/perfetto/trace/track_event/process_descriptor.proto
index fceb5ba..3ba9049 100644
--- a/protos/perfetto/trace/track_event/process_descriptor.proto
+++ b/protos/perfetto/trace/track_event/process_descriptor.proto
@@ -15,17 +15,17 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
 // Describes a process's attributes. Emitted as part of a TrackDescriptor,
 // usually by the process's main thread.
 //
-// Next id: 6.
+// Next id: 7.
 message ProcessDescriptor {
   optional int32 pid = 1;
   repeated string cmdline = 2;
+  optional string process_name = 6;
 
   optional int32 process_priority = 5;
 
diff --git a/protos/perfetto/trace/track_event/source_location.proto b/protos/perfetto/trace/track_event/source_location.proto
index 39d6bb1..81ed7cf 100644
--- a/protos/perfetto/trace/track_event/source_location.proto
+++ b/protos/perfetto/trace/track_event/source_location.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/task_execution.proto b/protos/perfetto/trace/track_event/task_execution.proto
index 6eebad9..77f4599 100644
--- a/protos/perfetto/trace/track_event/task_execution.proto
+++ b/protos/perfetto/trace/track_event/task_execution.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/thread_descriptor.proto b/protos/perfetto/trace/track_event/thread_descriptor.proto
index 1c14e21..594c1b2 100644
--- a/protos/perfetto/trace/track_event/thread_descriptor.proto
+++ b/protos/perfetto/trace/track_event/thread_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace/track_event/track_descriptor.proto b/protos/perfetto/trace/track_event/track_descriptor.proto
index 35b1ce2..5254b28 100644
--- a/protos/perfetto/trace/track_event/track_descriptor.proto
+++ b/protos/perfetto/trace/track_event/track_descriptor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/track_event/chrome_process_descriptor.proto";
 import "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto";
@@ -25,9 +24,7 @@
 package perfetto.protos;
 
 // Defines a track for TrackEvents. Slices and instant events on the same track
-// will be nested based on their timestamps, see TrackEvent::Type. Additionally
-// events on tracks which represent the same thread (i.e., matching pid and tid
-// in ThreadDescriptor) will be merged onto one sequential timeline.
+// will be nested based on their timestamps, see TrackEvent::Type.
 //
 // A TrackDescriptor only needs to be emitted by one trace writer / producer and
 // is valid for the entirety of the trace. To ensure the descriptor isn't lost
@@ -57,10 +54,24 @@
   // Name of the track.
   optional string name = 2;
 
-  // Associate the track with a process or thread - the UI will merge all tracks
-  // for the same process / thread into a single timeline view.
+  // Associate the track with a process, making it the process-global track.
+  // There should only be one such track per process (usually for instant
+  // events; trace processor uses this fact to detect pid reuse). If you need
+  // more (e.g. for asynchronous events), create child tracks using parent_uuid.
+  //
+  // Trace processor will merge events on a process track with slice-type events
+  // from other sources (e.g. ftrace) for the same process into a single
+  // timeline view.
   optional ProcessDescriptor process = 3;
   optional ChromeProcessDescriptor chrome_process = 6;
+
+  // Associate the track with a thread, indicating that the track's events
+  // describe synchronous code execution on the thread. There should only be one
+  // such track per thread (trace processor uses this fact to detect tid reuse).
+  //
+  // Trace processor will merge events on a thread track with slice-type events
+  // from other sources (e.g. ftrace) for the same thread into a single timeline
+  // view.
   optional ThreadDescriptor thread = 4;
   optional ChromeThreadDescriptor chrome_thread = 7;
 }
diff --git a/protos/perfetto/trace/track_event/track_event.proto b/protos/perfetto/trace/track_event/track_event.proto
index 3d4b635..c566648 100644
--- a/protos/perfetto/trace/track_event/track_event.proto
+++ b/protos/perfetto/trace/track_event/track_event.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 import "protos/perfetto/trace/track_event/debug_annotation.proto";
 import "protos/perfetto/trace/track_event/log_message.proto";
diff --git a/protos/perfetto/trace/trigger.proto b/protos/perfetto/trace/trigger.proto
index d895d80..759eb4f 100644
--- a/protos/perfetto/trace/trigger.proto
+++ b/protos/perfetto/trace/trigger.proto
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace_processor/BUILD.gn b/protos/perfetto/trace_processor/BUILD.gn
index e645a3a..461fd9c 100644
--- a/protos/perfetto/trace_processor/BUILD.gn
+++ b/protos/perfetto/trace_processor/BUILD.gn
@@ -25,7 +25,5 @@
 
 perfetto_proto_library("metrics_impl_zero") {
   proto_generators = [ "zero" ]
-  sources = [
-    "metrics_impl.proto",
-  ]
+  sources = [ "metrics_impl.proto" ]
 }
diff --git a/protos/perfetto/trace_processor/metrics_impl.proto b/protos/perfetto/trace_processor/metrics_impl.proto
index 6ff42f5..281ced8 100644
--- a/protos/perfetto/trace_processor/metrics_impl.proto
+++ b/protos/perfetto/trace_processor/metrics_impl.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index 45804b7..d24d202 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.trace_processor.protos;
 
diff --git a/protos/third_party/pprof/BUILD.gn b/protos/third_party/pprof/BUILD.gn
index 18c24df..553d0ac 100644
--- a/protos/third_party/pprof/BUILD.gn
+++ b/protos/third_party/pprof/BUILD.gn
@@ -16,7 +16,5 @@
 
 perfetto_proto_library("@TYPE@") {
   proto_generators = [ "zero" ]
-  sources = [
-    "profile.proto",
-  ]
+  sources = [ "profile.proto" ]
 }
diff --git a/protos/third_party/pprof/profile.proto b/protos/third_party/pprof/profile.proto
index 30a4f6a..23aadc4 100644
--- a/protos/third_party/pprof/profile.proto
+++ b/protos/third_party/pprof/profile.proto
@@ -45,11 +45,6 @@
 option java_package = "com.google.perftools.profiles";
 option java_outer_classname = "ProfileProto";
 
-// Perfetto Changes ===========================================================
-// 1. Add optimize_for = LITE_RUNTIME
-option optimize_for = LITE_RUNTIME;
-// ============================================================================
-
 message Profile {
   // A description of the samples associated with each Sample.value.
   // For a cpu profile this might be:
diff --git a/src/android_internal/BUILD.gn b/src/android_internal/BUILD.gn
index 4306b16..956e07c 100644
--- a/src/android_internal/BUILD.gn
+++ b/src/android_internal/BUILD.gn
@@ -39,9 +39,7 @@
 }
 
 source_set("lazy_library_loader") {
-  public_deps = [
-    ":headers",
-  ]
+  public_deps = [ ":headers" ]
   deps = [
     "../../gn:default_deps",
     "../../src/base",
@@ -90,8 +88,8 @@
   # This target should never depend on any other perfetto target to avoid ODR
   # violation by doubly linking code in two .so(s) loaded in the same exe.
   assert_no_deps = [
+    "//include/*",
     "//src/base/*",
     "//src/tracing/*",
-    "//include/*",
   ]
 }
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index c786391..b1e4062 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -20,9 +20,7 @@
 enable_stack_trace = is_debug && perfetto_build_standalone && !is_wasm
 
 source_set("base") {
-  deps = [
-    "../../gn:default_deps",
-  ]
+  deps = [ "../../gn:default_deps" ]
   public_deps = [
     "../../include/perfetto/base",
     "../../include/perfetto/ext/base",
@@ -61,9 +59,7 @@
 
 if (enable_stack_trace) {
   source_set("debug_crash_stack_trace") {
-    sources = [
-      "debug_crash_stack_trace.cc",
-    ]
+    sources = [ "debug_crash_stack_trace.cc" ]
     deps = [
       "../../gn:default_deps",
       "../../include/perfetto/ext/base",
@@ -84,9 +80,7 @@
       "../../include/perfetto/ext/base",
       "../../include/perfetto/ext/base",
     ]
-    sources = [
-      "unix_socket.cc",
-    ]
+    sources = [ "unix_socket.cc" ]
   }
 }
 
@@ -165,8 +159,6 @@
       "../../gn:benchmark",
       "../../gn:default_deps",
     ]
-    sources = [
-      "flat_set_benchmark.cc",
-    ]
+    sources = [ "flat_set_benchmark.cc" ]
   }
 }
diff --git a/src/base/flat_set_benchmark.cc b/src/base/flat_set_benchmark.cc
index 12e5565..0773fbe 100644
--- a/src/base/flat_set_benchmark.cc
+++ b/src/base/flat_set_benchmark.cc
@@ -18,7 +18,7 @@
 
 #include <benchmark/benchmark.h>
 
-#include "perfetto/ext/base/flat_set.h"
+#include "perfetto/base/flat_set.h"
 
 namespace {
 
diff --git a/src/base/flat_set_unittest.cc b/src/base/flat_set_unittest.cc
index c853e13..406e05c 100644
--- a/src/base/flat_set_unittest.cc
+++ b/src/base/flat_set_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "perfetto/ext/base/flat_set.h"
+#include "perfetto/base/flat_set.h"
 
 #include <random>
 #include <set>
@@ -38,7 +38,9 @@
     EXPECT_EQ(flat_set.find(42), flat_set.end());
     EXPECT_EQ(flat_set.find(42), flat_set.begin());
 
-    flat_set.insert(1);
+    auto it_and_inserted = flat_set.insert(1);
+    EXPECT_EQ(it_and_inserted.first, flat_set.find(1));
+    EXPECT_TRUE(it_and_inserted.second);
     EXPECT_FALSE(flat_set.empty());
     EXPECT_EQ(flat_set.size(), 1u);
     {
@@ -52,7 +54,9 @@
     EXPECT_NE(flat_set.begin(), flat_set.end());
     EXPECT_EQ(std::distance(flat_set.begin(), flat_set.end()), 1);
 
-    flat_set.insert(1);
+    it_and_inserted = flat_set.insert(1);
+    EXPECT_EQ(it_and_inserted.first, flat_set.find(1));
+    EXPECT_FALSE(it_and_inserted.second);
     EXPECT_EQ(flat_set.size(), 1u);
     EXPECT_TRUE(flat_set.count(1));
     EXPECT_FALSE(flat_set.count(0));
@@ -62,10 +66,10 @@
     EXPECT_FALSE(flat_set.count(1));
     EXPECT_EQ(flat_set.size(), 0u);
 
-    flat_set.insert(7);
-    flat_set.insert(-4);
-    flat_set.insert(11);
-    flat_set.insert(-13);
+    EXPECT_TRUE(flat_set.insert(7).second);
+    EXPECT_TRUE(flat_set.insert(-4).second);
+    EXPECT_TRUE(flat_set.insert(11).second);
+    EXPECT_TRUE(flat_set.insert(-13).second);
     EXPECT_TRUE(flat_set.count(7));
     EXPECT_TRUE(flat_set.count(-4));
     EXPECT_TRUE(flat_set.count(11));
@@ -94,8 +98,11 @@
   for (int i = 0; i < 10000; i++) {
     const int val = int_dist(rng);
     if (i % 3) {
-      flat_set.insert(val);
-      gold_set.insert(val);
+      auto flat_result = flat_set.insert(val);
+      auto gold_result = gold_set.insert(val);
+      EXPECT_EQ(flat_result.first, flat_set.find(val));
+      EXPECT_EQ(gold_result.first, gold_set.find(val));
+      EXPECT_EQ(flat_result.second, gold_result.second);
     } else {
       flat_set.erase(val);
       gold_set.erase(val);
diff --git a/src/base/logging.cc b/src/base/logging.cc
index d38e263..b733988 100644
--- a/src/base/logging.cc
+++ b/src/base/logging.cc
@@ -125,11 +125,11 @@
   // to correlated events across differrent processses (e.g. traced and
   // traced_probes). The wall time % 1000 is good enough.
   char timestamp[32];
-  int t_ms = static_cast<int>(GetWallTimeMs().count());
-  int t_sec = t_ms / 1000;
+  uint32_t t_ms = static_cast<uint32_t>(GetWallTimeMs().count());
+  uint32_t t_sec = t_ms / 1000;
   t_ms -= t_sec * 1000;
   t_sec = t_sec % 1000;
-  snprintf(timestamp, sizeof(timestamp), "[%03d.%03d] ", t_sec, t_ms);
+  snprintf(timestamp, sizeof(timestamp), "[%03u.%03u] ", t_sec, t_ms);
 
   if (use_colors) {
     fprintf(stderr, "%s%s%s%s %s%s%s\n", kLightGray, timestamp, file_and_line,
diff --git a/src/base/paged_memory.cc b/src/base/paged_memory.cc
index a214de3..c7feb53 100644
--- a/src/base/paged_memory.cc
+++ b/src/base/paged_memory.cc
@@ -113,16 +113,18 @@
   PERFETTO_DCHECK(p_);
   PERFETTO_DCHECK(p >= p_);
   PERFETTO_DCHECK(static_cast<char*>(p) + size <= p_ + size_);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   // Discarding pages on Windows has more CPU cost than is justified for the
   // possible memory savings.
   return false;
-#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   // http://man7.org/linux/man-pages/man2/madvise.2.html
   int res = madvise(p, size, MADV_DONTNEED);
   PERFETTO_DCHECK(res == 0);
   return true;
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
 }
 
 #if TRACK_COMMITTED_SIZE()
diff --git a/src/base/paged_memory_unittest.cc b/src/base/paged_memory_unittest.cc
index 97f1a74..06695d6 100644
--- a/src/base/paged_memory_unittest.cc
+++ b/src/base/paged_memory_unittest.cc
@@ -83,7 +83,7 @@
 
     // Next page shouldn't be mapped.
     ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw + kMappedSize, 4096));
-    EXPECT_DEATH({ ptr_raw[kMappedSize] = 'x'; }, ".*");
+    EXPECT_DEATH_IF_SUPPORTED({ ptr_raw[kMappedSize] = 'x'; }, ".*");
 
     // Commit the remaining pages.
     mem.EnsureCommitted(kSize);
@@ -119,7 +119,7 @@
 
 #if defined(ADDRESS_SANITIZER)
 TEST(PagedMemoryTest, AccessUncommittedMemoryTriggersASAN) {
-  EXPECT_DEATH(
+  EXPECT_DEATH_IF_SUPPORTED(
       {
         constexpr size_t kNumPages = 4096;
         constexpr size_t kSize = 4096 * kNumPages;
@@ -141,8 +141,8 @@
   PagedMemory mem = PagedMemory::Allocate(kSize);
   ASSERT_TRUE(mem.IsValid());
   volatile char* raw = reinterpret_cast<char*>(mem.Get());
-  EXPECT_DEATH({ raw[-1] = 'x'; }, ".*");
-  EXPECT_DEATH({ raw[kSize] = 'x'; }, ".*");
+  EXPECT_DEATH_IF_SUPPORTED({ raw[-1] = 'x'; }, ".*");
+  EXPECT_DEATH_IF_SUPPORTED({ raw[kSize] = 'x'; }, ".*");
 }
 
 // Disable this on:
diff --git a/src/base/scoped_file_unittest.cc b/src/base/scoped_file_unittest.cc
index 13f1acb..dff4413 100644
--- a/src/base/scoped_file_unittest.cc
+++ b/src/base/scoped_file_unittest.cc
@@ -170,7 +170,7 @@
 #ifdef TEST_INVALID_CLOSE
 TEST(ScopedFileTest, CloseFailureIsFatal) {
   int raw_fd = open(kNullFilename, O_RDONLY);
-  ASSERT_DEATH(
+  ASSERT_DEATH_IF_SUPPORTED(
       {
         ScopedFile scoped_file(raw_fd);
         ASSERT_EQ(0, close(raw_fd));
diff --git a/src/base/task_runner_unittest.cc b/src/base/task_runner_unittest.cc
index a8368b5..10f047b 100644
--- a/src/base/task_runner_unittest.cc
+++ b/src/base/task_runner_unittest.cc
@@ -30,16 +30,11 @@
 namespace base {
 namespace {
 
-template <typename T>
 class TaskRunnerTest : public ::testing::Test {
  public:
-  T task_runner;
+  UnixTaskRunner task_runner;
 };
 
-using TaskRunnerTypes = ::testing::Types<UnixTaskRunner>;
-
-TYPED_TEST_SUITE(TaskRunnerTest, TaskRunnerTypes);
-
 struct TestPipe : Pipe {
   TestPipe() : Pipe(Pipe::Create()) {
     // Make the pipe initially readable.
@@ -59,7 +54,7 @@
   }
 };
 
-TYPED_TEST(TaskRunnerTest, PostImmediateTask) {
+TEST_F(TaskRunnerTest, PostImmediateTask) {
   auto& task_runner = this->task_runner;
   int counter = 0;
   task_runner.PostTask([&counter] { counter = (counter << 4) | 1; });
@@ -71,7 +66,7 @@
   EXPECT_EQ(0x1234, counter);
 }
 
-TYPED_TEST(TaskRunnerTest, PostDelayedTask) {
+TEST_F(TaskRunnerTest, PostDelayedTask) {
   auto& task_runner = this->task_runner;
   int counter = 0;
   task_runner.PostDelayedTask([&counter] { counter = (counter << 4) | 1; }, 5);
@@ -83,7 +78,7 @@
   EXPECT_EQ(0x1234, counter);
 }
 
-TYPED_TEST(TaskRunnerTest, PostImmediateTaskFromTask) {
+TEST_F(TaskRunnerTest, PostImmediateTaskFromTask) {
   auto& task_runner = this->task_runner;
   task_runner.PostTask([&task_runner] {
     task_runner.PostTask([&task_runner] { task_runner.Quit(); });
@@ -91,7 +86,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, PostDelayedTaskFromTask) {
+TEST_F(TaskRunnerTest, PostDelayedTaskFromTask) {
   auto& task_runner = this->task_runner;
   task_runner.PostTask([&task_runner] {
     task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
@@ -99,7 +94,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, PostImmediateTaskFromOtherThread) {
+TEST_F(TaskRunnerTest, PostImmediateTaskFromOtherThread) {
   auto& task_runner = this->task_runner;
   ThreadChecker thread_checker;
   int counter = 0;
@@ -118,7 +113,7 @@
   EXPECT_EQ(0x1234, counter);
 }
 
-TYPED_TEST(TaskRunnerTest, PostDelayedTaskFromOtherThread) {
+TEST_F(TaskRunnerTest, PostDelayedTaskFromOtherThread) {
   auto& task_runner = this->task_runner;
   std::thread thread([&task_runner] {
     task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
@@ -127,7 +122,7 @@
   thread.join();
 }
 
-TYPED_TEST(TaskRunnerTest, AddFileDescriptorWatch) {
+TEST_F(TaskRunnerTest, AddFileDescriptorWatch) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
   task_runner.AddFileDescriptorWatch(pipe.rd.get(),
@@ -135,7 +130,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, RemoveFileDescriptorWatch) {
+TEST_F(TaskRunnerTest, RemoveFileDescriptorWatch) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
 
@@ -149,7 +144,7 @@
   EXPECT_FALSE(watch_ran);
 }
 
-TYPED_TEST(TaskRunnerTest, RemoveFileDescriptorWatchFromTask) {
+TEST_F(TaskRunnerTest, RemoveFileDescriptorWatchFromTask) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
 
@@ -165,7 +160,7 @@
   EXPECT_FALSE(watch_ran);
 }
 
-TYPED_TEST(TaskRunnerTest, AddFileDescriptorWatchFromAnotherWatch) {
+TEST_F(TaskRunnerTest, AddFileDescriptorWatchFromAnotherWatch) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
   TestPipe pipe2;
@@ -179,7 +174,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, RemoveFileDescriptorWatchFromAnotherWatch) {
+TEST_F(TaskRunnerTest, RemoveFileDescriptorWatchFromAnotherWatch) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
   TestPipe pipe2;
@@ -198,7 +193,7 @@
   EXPECT_FALSE(watch_ran);
 }
 
-TYPED_TEST(TaskRunnerTest, ReplaceFileDescriptorWatchFromAnotherWatch) {
+TEST_F(TaskRunnerTest, ReplaceFileDescriptorWatchFromAnotherWatch) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
   TestPipe pipe2;
@@ -216,7 +211,7 @@
   EXPECT_FALSE(watch_ran);
 }
 
-TYPED_TEST(TaskRunnerTest, AddFileDescriptorWatchFromAnotherThread) {
+TEST_F(TaskRunnerTest, AddFileDescriptorWatchFromAnotherThread) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
 
@@ -228,7 +223,7 @@
   thread.join();
 }
 
-TYPED_TEST(TaskRunnerTest, FileDescriptorWatchWithMultipleEvents) {
+TEST_F(TaskRunnerTest, FileDescriptorWatchWithMultipleEvents) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
 
@@ -246,7 +241,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, FileDescriptorClosedEvent) {
+TEST_F(TaskRunnerTest, FileDescriptorClosedEvent) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
   pipe.wr.reset();
@@ -255,7 +250,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, PostManyDelayedTasks) {
+TEST_F(TaskRunnerTest, PostManyDelayedTasks) {
   // Check that PostTask doesn't start failing if there are too many scheduled
   // wake-ups.
   auto& task_runner = this->task_runner;
@@ -265,7 +260,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, RunAgain) {
+TEST_F(TaskRunnerTest, RunAgain) {
   auto& task_runner = this->task_runner;
   int counter = 0;
   task_runner.PostTask([&task_runner, &counter] {
@@ -281,31 +276,28 @@
   EXPECT_EQ(2, counter);
 }
 
-template <typename TaskRunner>
-void RepeatingTask(TaskRunner* task_runner) {
-  task_runner->PostTask(std::bind(&RepeatingTask<TaskRunner>, task_runner));
+void RepeatingTask(UnixTaskRunner* task_runner) {
+  task_runner->PostTask(std::bind(&RepeatingTask, task_runner));
 }
 
-TYPED_TEST(TaskRunnerTest, FileDescriptorWatchesNotStarved) {
+TEST_F(TaskRunnerTest, FileDescriptorWatchesNotStarved) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
-  task_runner.PostTask(std::bind(&RepeatingTask<TypeParam>, &task_runner));
+  task_runner.PostTask(std::bind(&RepeatingTask, &task_runner));
   task_runner.AddFileDescriptorWatch(pipe.rd.get(),
                                      [&task_runner] { task_runner.Quit(); });
   task_runner.Run();
 }
 
-template <typename TaskRunner>
-void CountdownTask(TaskRunner* task_runner, int* counter) {
+void CountdownTask(UnixTaskRunner* task_runner, int* counter) {
   if (!--(*counter)) {
     task_runner->Quit();
     return;
   }
-  task_runner->PostTask(
-      std::bind(&CountdownTask<TaskRunner>, task_runner, counter));
+  task_runner->PostTask(std::bind(&CountdownTask, task_runner, counter));
 }
 
-TYPED_TEST(TaskRunnerTest, NoDuplicateFileDescriptorWatchCallbacks) {
+TEST_F(TaskRunnerTest, NoDuplicateFileDescriptorWatchCallbacks) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
   bool watch_called = 0;
@@ -315,12 +307,11 @@
     pipe.Read();
     watch_called = true;
   });
-  task_runner.PostTask(
-      std::bind(&CountdownTask<TypeParam>, &task_runner, &counter));
+  task_runner.PostTask(std::bind(&CountdownTask, &task_runner, &counter));
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, ReplaceFileDescriptorWatchFromOtherThread) {
+TEST_F(TaskRunnerTest, ReplaceFileDescriptorWatchFromOtherThread) {
   auto& task_runner = this->task_runner;
   TestPipe pipe;
 
@@ -339,7 +330,7 @@
   thread.join();
 }
 
-TYPED_TEST(TaskRunnerTest, IsIdleForTesting) {
+TEST_F(TaskRunnerTest, IsIdleForTesting) {
   auto& task_runner = this->task_runner;
   task_runner.PostTask(
       [&task_runner] { EXPECT_FALSE(task_runner.IsIdleForTesting()); });
@@ -350,7 +341,7 @@
   task_runner.Run();
 }
 
-TYPED_TEST(TaskRunnerTest, RunsTasksOnCurrentThread) {
+TEST_F(TaskRunnerTest, RunsTasksOnCurrentThread) {
   auto& main_tr = this->task_runner;
 
   EXPECT_TRUE(main_tr.RunsTasksOnCurrentThread());
diff --git a/src/base/test/benchmark_main.cc b/src/base/test/benchmark_main.cc
index e410e52..3a3feaa 100644
--- a/src/base/test/benchmark_main.cc
+++ b/src/base/test/benchmark_main.cc
@@ -14,4 +14,4 @@
 
 #include <benchmark/benchmark.h>
 
-BENCHMARK_MAIN();
+BENCHMARK_MAIN()
diff --git a/src/base/test/utils.h b/src/base/test/utils.h
index c2125e7..9f61eac 100644
--- a/src/base/test/utils.h
+++ b/src/base/test/utils.h
@@ -23,15 +23,27 @@
 
 #if PERFETTO_DCHECK_IS_ON()
 
-#define EXPECT_DCHECK_DEATH(statement) EXPECT_DEATH(statement, "PERFETTO_CHECK")
-#define ASSERT_DCHECK_DEATH(statement) ASSERT_DEATH(statement, "PERFETTO_CHECK")
+#define EXPECT_DCHECK_DEATH(statement) \
+  EXPECT_DEATH_IF_SUPPORTED(statement, "PERFETTO_CHECK")
+#define ASSERT_DCHECK_DEATH(statement) \
+  ASSERT_DEATH_IF_SUPPORTED(statement, "PERFETTO_CHECK")
 
 #else  // PERFETTO_DCHECK_IS_ON()
 
+// Since PERFETTO_DCHECK_IS_ON() is false these statements should not die (if
+// they should/do we should use EXPECT/ASSERT DEATH_TEST_IF_SUPPORTED directly).
+// Therefore if the platform supports DEATH_TESTS we can use the handy
+// GTEST_EXECUTE_STATEMENT_ which prevents optimizing the code away, and if not
+// we just fall back on executing the code directly.
+#if defined(GTEST_EXECUTE_STATEMENT_)
 #define EXPECT_DCHECK_DEATH(statement) \
     GTEST_EXECUTE_STATEMENT_(statement, "PERFETTO_CHECK")
 #define ASSERT_DCHECK_DEATH(statement) \
     GTEST_EXECUTE_STATEMENT_(statement, "PERFETTO_CHECK")
+#else
+#define EXPECT_DCHECK_DEATH(statement) [&]() { statement }()
+#define ASSERT_DCHECK_DEATH(statement) [&]() { statement }()
+#endif  //  defined(GTEST_EXECUTE_STATEMENT_)
 
 #endif  // PERFETTO_DCHECK_IS_ON()
 
diff --git a/src/base/thread_task_runner.cc b/src/base/thread_task_runner.cc
index 0576ee9..d1a3b93 100644
--- a/src/base/thread_task_runner.cc
+++ b/src/base/thread_task_runner.cc
@@ -27,6 +27,11 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+
 namespace perfetto {
 namespace base {
 
@@ -52,7 +57,7 @@
     thread_.join();
 }
 
-ThreadTaskRunner::ThreadTaskRunner() {
+ThreadTaskRunner::ThreadTaskRunner(const std::string& name) : name_(name) {
   std::mutex init_lock;
   std::condition_variable init_cv;
 
@@ -66,6 +71,7 @@
         // notifying).
         init_cv.notify_one();
       };
+
   thread_ = std::thread(&ThreadTaskRunner::RunTaskThread, this,
                         std::move(initializer));
 
@@ -75,11 +81,44 @@
 
 void ThreadTaskRunner::RunTaskThread(
     std::function<void(UnixTaskRunner*)> initializer) {
+  if (!name_.empty()) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+    pthread_setname_np(name_.c_str());
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+    prctl(PR_SET_NAME, name_.c_str());
+#endif
+  }
+
   UnixTaskRunner task_runner;
   task_runner.PostTask(std::bind(std::move(initializer), &task_runner));
   task_runner.Run();
 }
 
+void ThreadTaskRunner::PostTaskAndWaitForTesting(std::function<void()> fn) {
+  std::mutex mutex;
+  std::condition_variable cv;
+
+  std::unique_lock<std::mutex> lock(mutex);
+  bool done = false;
+  task_runner_->PostTask([&mutex, &cv, &done, &fn] {
+    fn();
+
+    std::lock_guard<std::mutex> inner_lock(mutex);
+    done = true;
+    cv.notify_one();
+  });
+  cv.wait(lock, [&done] { return done; });
+}
+
+uint64_t ThreadTaskRunner::GetThreadCPUTimeNsForTesting() {
+  uint64_t thread_time_ns = 0;
+  PostTaskAndWaitForTesting([&thread_time_ns] {
+    thread_time_ns = static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
+  });
+  return thread_time_ns;
+}
+
 }  // namespace base
 }  // namespace perfetto
 
diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
index e63f978..f6db62a 100644
--- a/src/base/unix_socket.cc
+++ b/src/base/unix_socket.cc
@@ -172,6 +172,15 @@
 }
 
 // static
+UnixSocketRaw UnixSocketRaw::CreateMayFail(SockFamily family, SockType type) {
+  auto fd = ScopedFile(socket(GetSockFamily(family), GetSockType(type), 0));
+  if (!fd) {
+    return UnixSocketRaw();
+  }
+  return UnixSocketRaw(std::move(fd), family, type);
+}
+
+// static
 std::pair<UnixSocketRaw, UnixSocketRaw> UnixSocketRaw::CreatePair(
     SockFamily family,
     SockType type) {
diff --git a/src/base/watchdog_posix.cc b/src/base/watchdog_posix.cc
index 401ecdb..25782d9 100644
--- a/src/base/watchdog_posix.cc
+++ b/src/base/watchdog_posix.cc
@@ -28,8 +28,8 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/thread_utils.h"
 
 namespace perfetto {
 namespace base {
@@ -47,7 +47,8 @@
   for (size_t i = 0; i < size; i++) {
     total += array[i];
   }
-  return total / size;
+  return static_cast<double>(total / size);
+
 }
 
 }  //  namespace
@@ -164,7 +165,7 @@
   // Add the current stat value to the ring buffer and check that the mean
   // remains under our threshold.
   if (memory_window_bytes_.Push(rss_bytes)) {
-    if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
+    if (memory_window_bytes_.Mean() > static_cast<double>(memory_limit_bytes_)) {
       PERFETTO_ELOG(
           "Memory watchdog trigger. Memory window of %f bytes is above the "
           "%" PRIu64 " bytes limit.",
@@ -187,7 +188,7 @@
     double window_interval_ticks =
         (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
          1000.0) *
-        sysconf(_SC_CLK_TCK);
+        static_cast<double>(sysconf(_SC_CLK_TCK));
     double percentage = static_cast<double>(difference_ticks) /
                         static_cast<double>(window_interval_ticks) * 100;
     if (percentage > cpu_limit_percentage_) {
diff --git a/src/base/watchdog_unittest.cc b/src/base/watchdog_unittest.cc
index f3bc850..81d0ac6 100644
--- a/src/base/watchdog_unittest.cc
+++ b/src/base/watchdog_unittest.cc
@@ -17,9 +17,9 @@
 #include "perfetto/ext/base/watchdog.h"
 
 #include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/thread_utils.h"
 #include "test/gtest_and_gmock.h"
 
 #include <signal.h>
@@ -116,7 +116,7 @@
   return sigaction(SIGABRT, act, nullptr);
 }
 
-PlatformThreadID g_aborted_thread = 0;
+PlatformThreadId g_aborted_thread = 0;
 void SIGABRTHandler(int) {
   g_aborted_thread = GetThreadId();
 }
@@ -138,7 +138,7 @@
   std::condition_variable cv;
   bool quit = false;
   g_aborted_thread = 0;
-  PlatformThreadID expected_tid = 0;
+  PlatformThreadId expected_tid = 0;
 
   auto thread_fn = [&mutex, &cv, &quit, &expected_tid](size_t thread_num) {
     if (thread_num == kKillThreadNum) {
diff --git a/src/ipc/BUILD.gn b/src/ipc/BUILD.gn
index 3e48bb0..d8ee2b4 100644
--- a/src/ipc/BUILD.gn
+++ b/src/ipc/BUILD.gn
@@ -21,45 +21,74 @@
 # projects should be depending on our IPC layer.
 assert(enable_perfetto_ipc)
 
-source_set("ipc") {
-  public_configs = [ "../../gn:default_config" ]
+source_set("client") {
   public_deps = [
     "../../include/perfetto/ext/ipc",
     "../base:unix_socket",
   ]
   deps = [
+    ":common",
     "../../gn:default_deps",
     "../../protos/perfetto/ipc:wire_protocol_cpp",
     "../base",
   ]
   sources = [
-    "buffered_frame_deserializer.cc",
-    "buffered_frame_deserializer.h",
     "client_impl.cc",
     "client_impl.h",
-    "deferred.cc",
+    "service_proxy.cc",
+  ]
+}
+
+source_set("host") {
+  public_deps = [
+    "../../include/perfetto/ext/ipc",
+    "../base:unix_socket",
+  ]
+  deps = [
+    ":common",
+    "../../gn:default_deps",
+    "../../protos/perfetto/ipc:wire_protocol_cpp",
+    "../base",
+  ]
+  sources = [
     "host_impl.cc",
     "host_impl.h",
-    "service_proxy.cc",
+  ]
+}
+
+source_set("common") {
+  public_deps = [
+    "../../include/perfetto/ext/ipc",
+    "../../protos/perfetto/ipc:wire_protocol_cpp",
+  ]
+  deps = [
+    "../../gn:default_deps",
+    "../base",
+  ]
+  sources = [
+    "buffered_frame_deserializer.cc",
+    "buffered_frame_deserializer.h",
+    "deferred.cc",
     "virtual_destructors.cc",
   ]
 }
 
 perfetto_fuzzer_test("buffered_frame_deserializer_fuzzer") {
-  sources = [
-    "buffered_frame_deserializer_fuzzer.cc",
-  ]
+  sources = [ "buffered_frame_deserializer_fuzzer.cc" ]
   deps = [
-    ":ipc",
+    ":common",
     "../../gn:default_deps",
     "../../protos/perfetto/ipc:wire_protocol_cpp",
+    "../base",
   ]
 }
 
 perfetto_unittest_source_set("unittests") {
   testonly = true
   deps = [
-    ":ipc",
+    ":client",
+    ":common",
+    ":host",
     ":test_messages_cpp",
     ":test_messages_ipc",
     "../../gn:default_deps",
@@ -94,7 +123,8 @@
 static_library("perfetto_ipc") {
   complete_static_lib = true
   deps = [
-    ":ipc",
+    ":client",
+    ":host",
     "../../gn:default_deps",
   ]
 }
diff --git a/src/ipc/client_impl.cc b/src/ipc/client_impl.cc
index e2dbc35..f38ef6d 100644
--- a/src/ipc/client_impl.cc
+++ b/src/ipc/client_impl.cc
@@ -27,6 +27,8 @@
 #include "perfetto/ext/ipc/service_descriptor.h"
 #include "perfetto/ext/ipc/service_proxy.h"
 
+#include "protos/perfetto/ipc/wire_protocol.gen.h"
+
 // TODO(primiano): Add ThreadChecker everywhere.
 
 // TODO(primiano): Add timeouts.
diff --git a/src/ipc/client_impl.h b/src/ipc/client_impl.h
index 35dea50..ff9c7bc 100644
--- a/src/ipc/client_impl.h
+++ b/src/ipc/client_impl.h
@@ -17,20 +17,25 @@
 #ifndef SRC_IPC_CLIENT_IMPL_H_
 #define SRC_IPC_CLIENT_IMPL_H_
 
+#include <list>
+#include <map>
+#include <memory>
+
 #include "perfetto/base/task_runner.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/ipc/client.h"
 #include "src/ipc/buffered_frame_deserializer.h"
 
-#include "protos/perfetto/ipc/wire_protocol.gen.h"
-
-#include <list>
-#include <map>
-#include <memory>
-
 namespace perfetto {
 
+namespace protos {
+namespace gen {
+class IPCFrame_BindServiceReply;
+class IPCFrame_InvokeMethodReply;
+}  // namespace gen
+}  // namespace protos
+
 namespace base {
 class TaskRunner;
 }  // namespace base
@@ -78,8 +83,10 @@
 
   bool SendFrame(const Frame&, int fd = -1);
   void OnFrameReceived(const Frame&);
-  void OnBindServiceReply(QueuedRequest, const Frame::BindServiceReply&);
-  void OnInvokeMethodReply(QueuedRequest, const Frame::InvokeMethodReply&);
+  void OnBindServiceReply(QueuedRequest,
+                          const protos::gen::IPCFrame_BindServiceReply&);
+  void OnInvokeMethodReply(QueuedRequest,
+                           const protos::gen::IPCFrame_InvokeMethodReply&);
 
   bool invoking_method_reply_ = false;
   std::unique_ptr<base::UnixSocket> sock_;
diff --git a/src/ipc/client_impl_unittest.cc b/src/ipc/client_impl_unittest.cc
index 42a5dd2..08ea09c 100644
--- a/src/ipc/client_impl_unittest.cc
+++ b/src/ipc/client_impl_unittest.cc
@@ -32,6 +32,7 @@
 #include "src/ipc/test/test_socket.h"
 #include "test/gtest_and_gmock.h"
 
+#include "protos/perfetto/ipc/wire_protocol.gen.h"
 #include "src/ipc/test/client_unittest_messages.gen.h"
 
 namespace perfetto {
diff --git a/src/ipc/protoc_plugin/BUILD.gn b/src/ipc/protoc_plugin/BUILD.gn
index 535f53a..57dcb51 100644
--- a/src/ipc/protoc_plugin/BUILD.gn
+++ b/src/ipc/protoc_plugin/BUILD.gn
@@ -15,9 +15,7 @@
 import("../../../gn/perfetto_host_executable.gni")
 
 perfetto_host_executable("ipc_plugin") {
-  sources = [
-    "ipc_plugin.cc",
-  ]
+  sources = [ "ipc_plugin.cc" ]
   deps = [
     "../../../gn:default_deps",
     "../../../gn:protoc_lib",
diff --git a/src/ipc/service_proxy.cc b/src/ipc/service_proxy.cc
index e416f5f..7246a04 100644
--- a/src/ipc/service_proxy.cc
+++ b/src/ipc/service_proxy.cc
@@ -23,6 +23,8 @@
 #include "perfetto/ext/ipc/service_descriptor.h"
 #include "src/ipc/client_impl.h"
 
+#include "protos/perfetto/ipc/wire_protocol.gen.h"
+
 namespace perfetto {
 namespace ipc {
 
diff --git a/src/ipc/test/client_unittest_messages.proto b/src/ipc/test/client_unittest_messages.proto
index e19fa08..20c4623 100644
--- a/src/ipc/test/client_unittest_messages.proto
+++ b/src/ipc/test/client_unittest_messages.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.ipc;
 
diff --git a/src/ipc/test/deferred_unittest_messages.proto b/src/ipc/test/deferred_unittest_messages.proto
index 8fee32e..c09cc43 100644
--- a/src/ipc/test/deferred_unittest_messages.proto
+++ b/src/ipc/test/deferred_unittest_messages.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.ipc;
 
diff --git a/src/ipc/test/greeter_service.proto b/src/ipc/test/greeter_service.proto
index 8524ba7..724db63 100644
--- a/src/ipc/test/greeter_service.proto
+++ b/src/ipc/test/greeter_service.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 // Deliberately a namespace != of perfetto.* to spot namespace dependencies
 // bugs in the autogenerated headers.
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 969b732..4b06501 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -23,9 +23,7 @@
     ":perfetto_cmd",
     "../../gn:default_deps",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
 }
 
 # Tool to finalize long running traces.
@@ -39,15 +37,11 @@
     ":trigger_perfetto_cmd",
     "../../gn:default_deps",
   ]
-  sources = [
-    "trigger_perfetto_main.cc",
-  ]
+  sources = [ "trigger_perfetto_main.cc" ]
 }
 
 source_set("perfetto_atoms") {
-  sources = [
-    "perfetto_atoms.h",
-  ]
+  sources = [ "perfetto_atoms.h" ]
 }
 
 # Contains all the implementation but not the main() entry point. This target
@@ -61,15 +55,17 @@
     ":perfetto_atoms",
     ":trigger_producer",
     "../../gn:default_deps",
-    "../../gn:zlib",
     "../../protos/perfetto/common:cpp",
     "../../protos/perfetto/config:cpp",
     "../../protos/perfetto/config/ftrace:cpp",
     "../android_internal:lazy_library_loader",
     "../base",
     "../protozero",
-    "../tracing:ipc",
+    "../tracing/ipc/consumer",
   ]
+  if (enable_perfetto_zlib) {
+    deps += [ "../../gn:zlib" ]
+  }
   sources = [
     "config.cc",
     "config.h",
@@ -97,11 +93,9 @@
     ":trigger_producer",
     "../../gn:default_deps",
     "../base",
-    "../tracing:ipc",
+    "../tracing/ipc/producer",
   ]
-  sources = [
-    "trigger_perfetto.cc",
-  ]
+  sources = [ "trigger_perfetto.cc" ]
 }
 
 source_set("trigger_producer") {
@@ -112,15 +106,13 @@
   deps = [
     "../../gn:default_deps",
     "../base",
-    "../tracing:ipc",
+    "../tracing/ipc/producer",
   ]
 }
 
 perfetto_proto_library("protos") {
   proto_generators = [ "cpp" ]
-  sources = [
-    "perfetto_cmd_state.proto",
-  ]
+  sources = [ "perfetto_cmd_state.proto" ]
   proto_path = perfetto_root_path
 }
 
@@ -131,14 +123,16 @@
     ":perfetto_cmd",
     "../../gn:default_deps",
     "../../gn:gtest_and_gmock",
-    "../../gn:zlib",
     "../../include/perfetto/base",
     "../../include/perfetto/ext/base",
     "../../protos/perfetto/config:cpp",
     "../../protos/perfetto/config/ftrace:cpp",
     "../../protos/perfetto/trace:cpp",
-    "../tracing",
+    "../tracing/core",
   ]
+  if (enable_perfetto_zlib) {
+    deps += [ "../../gn:zlib" ]
+  }
   sources = [
     "config_unittest.cc",
     "packet_writer_unittest.cc",
diff --git a/src/perfetto_cmd/packet_writer.cc b/src/perfetto_cmd/packet_writer.cc
index 9d5a8d6..45aad47 100644
--- a/src/perfetto_cmd/packet_writer.cc
+++ b/src/perfetto_cmd/packet_writer.cc
@@ -24,13 +24,17 @@
 #include <stdio.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <zlib.h>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/protozero/proto_utils.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#endif
+
 namespace perfetto {
 namespace {
 
@@ -43,14 +47,20 @@
 // ID of the |packet| field in trace.proto. Hardcoded as this we don't
 // want to depend on protos/trace:lite for binary size saving reasons.
 constexpr uint32_t kPacketId = 1;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 // ID of |compressed_packets| in trace_packet.proto.
 constexpr uint32_t kCompressedPacketsId = 50;
 
 // Maximum allowable size for a single packet.
 const size_t kMaxPacketSize = 500 * 1024;
+
 // After every kPendingBytesLimit we do a Z_SYNC_FLUSH in the zlib stream.
 const size_t kPendingBytesLimit = 32 * 1024;
 
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 template <uint32_t id>
 size_t GetPreamble(size_t sz, Preamble* preamble) {
   uint8_t* ptr = reinterpret_cast<uint8_t*>(preamble->data());
@@ -73,6 +83,31 @@
   FILE* fd_;
 };
 
+FilePacketWriter::FilePacketWriter(FILE* fd) : fd_(fd) {}
+
+FilePacketWriter::~FilePacketWriter() {
+  fflush(fd_);
+}
+
+bool FilePacketWriter::WritePackets(const std::vector<TracePacket>& packets) {
+  for (const TracePacket& packet : packets) {
+    Preamble preamble;
+    size_t size = GetPreamble<kPacketId>(packet.size(), &preamble);
+    if (fwrite(preamble.data(), 1, size, fd_) != size)
+      return false;
+    for (const Slice& slice : packet.slices()) {
+      if (fwrite(reinterpret_cast<const char*>(slice.start), 1, slice.size,
+                 fd_) != slice.size) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 class ZipPacketWriter : public PacketWriter {
  public:
   ZipPacketWriter(std::unique_ptr<PacketWriter>);
@@ -102,29 +137,6 @@
   size_t pending_bytes_ = 0;
 };
 
-FilePacketWriter::FilePacketWriter(FILE* fd) : fd_(fd) {}
-
-FilePacketWriter::~FilePacketWriter() {
-  fflush(fd_);
-}
-
-bool FilePacketWriter::WritePackets(const std::vector<TracePacket>& packets) {
-  for (const TracePacket& packet : packets) {
-    Preamble preamble;
-    size_t size = GetPreamble<kPacketId>(packet.size(), &preamble);
-    if (fwrite(preamble.data(), 1, size, fd_) != size)
-      return false;
-    for (const Slice& slice : packet.slices()) {
-      if (fwrite(reinterpret_cast<const char*>(slice.start), 1, slice.size,
-                 fd_) != slice.size) {
-        return false;
-      }
-    }
-  }
-
-  return true;
-}
-
 ZipPacketWriter::ZipPacketWriter(std::unique_ptr<PacketWriter> writer)
     : writer_(std::move(writer)),
       buf_(base::PagedMemory::Allocate(kMaxPacketSize)),
@@ -179,7 +191,7 @@
   // Reinitialize the compresser if needed:
   if (!is_compressing_) {
     memset(&stream_, 0, sizeof(stream_));
-    CheckEq(deflateInit(&stream_, 9), Z_OK);
+    CheckEq(deflateInit(&stream_, 6), Z_OK);
     is_compressing_ = true;
     stream_.next_out = start_;
     stream_.avail_out = static_cast<unsigned int>(end_ - start_);
@@ -237,6 +249,8 @@
   pending_bytes_ += size;
 }
 
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 }  // namespace
 
 PacketWriter::PacketWriter() {}
@@ -247,9 +261,11 @@
   return std::unique_ptr<PacketWriter>(new FilePacketWriter(fd));
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 std::unique_ptr<PacketWriter> CreateZipPacketWriter(
     std::unique_ptr<PacketWriter> writer) {
   return std::unique_ptr<PacketWriter>(new ZipPacketWriter(std::move(writer)));
 }
+#endif
 
 }  // namespace perfetto
diff --git a/src/perfetto_cmd/packet_writer_unittest.cc b/src/perfetto_cmd/packet_writer_unittest.cc
index c1fab21..fbf9603 100644
--- a/src/perfetto_cmd/packet_writer_unittest.cc
+++ b/src/perfetto_cmd/packet_writer_unittest.cc
@@ -21,8 +21,7 @@
 
 #include <random>
 
-#include <zlib.h>
-
+#include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/temp_file.h"
@@ -34,6 +33,10 @@
 #include "protos/perfetto/trace/trace.gen.h"
 #include "protos/perfetto/trace/trace_packet.gen.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#endif
+
 namespace perfetto {
 namespace {
 
@@ -51,6 +54,7 @@
   return packet;
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 std::string Decompress(const std::string& data) {
   uint8_t out[1024];
 
@@ -76,6 +80,7 @@
   inflateEnd(&stream);
   return s;
 }
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 
 TEST(PacketWriterTest, FilePacketWriter) {
   base::TempFile tmp = base::TempFile::Create();
@@ -105,6 +110,8 @@
   EXPECT_EQ(trace.packet()[0].for_testing().str(), "abc");
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 TEST(PacketWriterTest, ZipPacketWriter) {
   base::TempFile tmp = base::TempFile::Create();
   base::ScopedResource<FILE*, fclose, nullptr> f(
@@ -281,5 +288,7 @@
   EXPECT_EQ(packet_count, 1000u);
 }
 
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index dff337c..bb48f6f 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -479,6 +479,10 @@
   if (trace_config_->trace_uuid_lsb() == 0 &&
       trace_config_->trace_uuid_msb() == 0) {
     base::Uuid uuid = base::Uuidv4();
+    if (trace_config_->statsd_metadata().triggering_subscription_id()) {
+      uuid.set_lsb(
+          trace_config_->statsd_metadata().triggering_subscription_id());
+    }
     uuid_ = uuid.ToString();
     trace_config_->set_trace_uuid_msb(uuid.msb());
     trace_config_->set_trace_uuid_lsb(uuid.lsb());
@@ -599,7 +603,11 @@
   if (trace_config_->compression_type() ==
       TraceConfig::COMPRESSION_TYPE_DEFLATE) {
     if (packet_writer_) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
       packet_writer_ = CreateZipPacketWriter(std::move(packet_writer_));
+#else
+      PERFETTO_ELOG("Cannot compress. Zlib not enabled in the build config");
+#endif
     } else {
       PERFETTO_ELOG("Cannot compress when tracing directly to file.");
     }
diff --git a/src/perfetto_cmd/perfetto_cmd_state.proto b/src/perfetto_cmd/perfetto_cmd_state.proto
index 1d0b14d..6337fb2 100644
--- a/src/perfetto_cmd/perfetto_cmd_state.proto
+++ b/src/perfetto_cmd/perfetto_cmd_state.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto;
 
diff --git a/src/perfetto_cmd/perfetto_config.descriptor.h b/src/perfetto_cmd/perfetto_config.descriptor.h
index c630e83..f9e012e 100644
--- a/src/perfetto_cmd/perfetto_config.descriptor.h
+++ b/src/perfetto_cmd/perfetto_config.descriptor.h
@@ -25,7 +25,7 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// f242f1ac484bbe7ba4c45e77b56ab588f8015196
+// d6628b15181dba5287e35b56b966b39ea93d42b1
 // SHA1(protos/perfetto/config/perfetto_config.proto)
 // 9bf7f741997f67724c7ee1b186b117708ac5228d
 
diff --git a/src/profiling/BUILD.gn b/src/profiling/BUILD.gn
index a11af0d..5a929b6 100644
--- a/src/profiling/BUILD.gn
+++ b/src/profiling/BUILD.gn
@@ -16,16 +16,12 @@
 import("../../gn/test.gni")
 
 source_set("deobfuscator") {
-  sources = [
-    "deobfuscator.cc",
-  ]
+  sources = [ "deobfuscator.cc" ]
   deps = [
     "../../gn:default_deps",
     "../../include/perfetto/ext/base:base",
   ]
-  public_deps = [
-    "../../include/perfetto/profiling:deobfuscator",
-  ]
+  public_deps = [ "../../include/perfetto/profiling:deobfuscator" ]
 }
 
 perfetto_unittest_source_set("unittests") {
@@ -37,7 +33,5 @@
     "../base",
     "../base:test_support",
   ]
-  sources = [
-    "deobfuscator_unittest.cc",
-  ]
+  sources = [ "deobfuscator_unittest.cc" ]
 }
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 1862fb4..6c4ef75 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -27,11 +27,9 @@
     "../../../src/base:unix_socket",
     "../../../src/profiling/memory:daemon",
     "../../../src/profiling/memory:wire_protocol",
-    "../../../src/tracing:ipc",
+    "../../../src/tracing/ipc/producer",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
 }
 
 # This library gets loaded into (and executes in) arbitrary android processes.
@@ -48,9 +46,7 @@
   shared_library("heapprofd_client") {
     configs -= [ "//gn/standalone:android_liblog" ]
     cflags = [ "-DPERFETTO_ANDROID_ASYNC_SAFE_LOG" ]
-    deps = [
-      ":malloc_hooks",
-    ]
+    deps = [ ":malloc_hooks" ]
   }
 
   # This will export publicly visible symbols for the malloc_hooks.
@@ -68,16 +64,12 @@
       "-isystem",
       rebase_path("../../../buildtools/bionic/libc", root_build_dir),
     ]
-    sources = [
-      "malloc_hooks.cc",
-    ]
+    sources = [ "malloc_hooks.cc" ]
   }
 }  # if (perfetto_build_with_android)
 
 source_set("wire_protocol") {
-  public_deps = [
-    "../../../gn:libunwindstack",
-  ]
+  public_deps = [ "../../../gn:libunwindstack" ]
   deps = [
     ":ring_buffer",
     "../../../gn:default_deps",
@@ -133,9 +125,7 @@
     "../../../gn:gtest_and_gmock",
     "../../base",
   ]
-  sources = [
-    "shared_ring_buffer_unittest.cc",
-  ]
+  sources = [ "shared_ring_buffer_unittest.cc" ]
 }
 
 source_set("daemon") {
@@ -148,8 +138,8 @@
     "../../../protos/perfetto/config/profiling:cpp",
     "../../base",
     "../../base:unix_socket",
-    "../../tracing",
-    "../../tracing:ipc",
+    "../../tracing/core",
+    "../../tracing/ipc/producer",
   ]
   public_deps = [
     "../../../gn:libunwindstack",
@@ -190,9 +180,7 @@
     "../../base",
     "../../base:unix_socket",
   ]
-  public_deps = [
-    "../../../gn:libunwindstack",
-  ]
+  public_deps = [ "../../../gn:libunwindstack" ]
   sources = [
     "client.cc",
     "client.h",
@@ -213,7 +201,7 @@
     "../../../include/perfetto/profiling:normalize",
     "../../base",
     "../../base:test_support",
-    "../../tracing",
+    "../../tracing/core",
   ]
   sources = [
     "bookkeeping_unittest.cc",
@@ -244,9 +232,7 @@
     "../../base",
     "../../base:test_support",
   ]
-  sources = [
-    "heapprofd_end_to_end_test.cc",
-  ]
+  sources = [ "heapprofd_end_to_end_test.cc" ]
   if (start_daemons_for_testing) {
     defines = [ "PERFETTO_START_DAEMONS_FOR_TESTING" ]
   }
@@ -254,24 +240,20 @@
 
 perfetto_fuzzer_test("unwinding_fuzzer") {
   testonly = true
-  sources = [
-    "unwinding_fuzzer.cc",
-  ]
+  sources = [ "unwinding_fuzzer.cc" ]
   deps = [
     ":daemon",
     ":ring_buffer",
     ":wire_protocol",
     "../../../gn:default_deps",
     "../../base",
-    "../../tracing",
+    "../../tracing/core",
   ]
 }
 
 perfetto_fuzzer_test("shared_ring_buffer_fuzzer") {
   testonly = true
-  sources = [
-    "shared_ring_buffer_fuzzer.cc",
-  ]
+  sources = [ "shared_ring_buffer_fuzzer.cc" ]
   deps = [
     ":ring_buffer",
     "../../../gn:default_deps",
@@ -281,9 +263,7 @@
 
 perfetto_fuzzer_test("shared_ring_buffer_write_fuzzer") {
   testonly = true
-  sources = [
-    "shared_ring_buffer_write_fuzzer.cc",
-  ]
+  sources = [ "shared_ring_buffer_write_fuzzer.cc" ]
   deps = [
     ":ring_buffer",
     "../../../gn:default_deps",
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
index 3b94ac6..ed65372 100644
--- a/src/profiling/memory/client.cc
+++ b/src/profiling/memory/client.cc
@@ -35,9 +35,9 @@
 #include <new>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/utils.h"
 #include "src/profiling/memory/sampler.h"
@@ -289,7 +289,8 @@
                           uint64_t alloc_size,
                           uint64_t alloc_address) {
   if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
-    PERFETTO_LOG("Detected post-fork child situation, stopping profiling.");
+    PERFETTO_LOG(
+        "Detected post-fork child situation. Not profiling the child.");
     return false;
   }
 
diff --git a/src/profiling/memory/client_unittest.cc b/src/profiling/memory/client_unittest.cc
index e2f0500..68b2ee7 100644
--- a/src/profiling/memory/client_unittest.cc
+++ b/src/profiling/memory/client_unittest.cc
@@ -18,7 +18,7 @@
 
 #include <thread>
 
-#include "perfetto/ext/base/thread_utils.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "test/gtest_and_gmock.h"
 
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index d698650..96d4f50 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -47,23 +47,6 @@
 using ::testing::Bool;
 using ::testing::Eq;
 
-class HeapprofdDelegate : public ThreadDelegate {
- public:
-  HeapprofdDelegate(const std::string& producer_socket)
-      : producer_socket_(producer_socket) {}
-  ~HeapprofdDelegate() override = default;
-
-  void Initialize(base::TaskRunner* task_runner) override {
-    producer_.reset(
-        new HeapprofdProducer(HeapprofdMode::kCentral, task_runner));
-    producer_->ConnectWithRetries(producer_socket_.c_str());
-  }
-
- private:
-  std::string producer_socket_;
-  std::unique_ptr<HeapprofdProducer> producer_;
-};
-
 constexpr const char* kHeapprofdModeProperty = "heapprofd.userdebug.mode";
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 2497451..e077f34 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -39,7 +39,6 @@
 
 constexpr char kHeapprofdDataSource[] = "android.heapprofd";
 constexpr size_t kUnwinderThreads = 5;
-constexpr int kHeapprofdSignal = 36;
 
 constexpr uint32_t kInitialConnectionBackoffMs = 100;
 constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
@@ -49,6 +48,10 @@
 constexpr uint64_t kDefaultShmemSize = 8 * 1048576;  // ~8 MB
 constexpr uint64_t kMaxShmemSize = 500 * 1048576;    // ~500 MB
 
+// Constants specified by bionic, hardcoded here for simplicity.
+constexpr int kProfilingSignal = __SIGRTMIN + 4;
+constexpr int kHeapprofdSignalValue = 0;
+
 std::vector<UnwindingWorker> MakeUnwindingWorkers(HeapprofdProducer* delegate,
                                                   size_t n) {
   std::vector<UnwindingWorker> ret;
@@ -406,9 +409,12 @@
       continue;
     }
 
-    PERFETTO_DLOG("Sending %d to %d", kHeapprofdSignal, pid);
-    if (kill(pid, kHeapprofdSignal) != 0) {
-      PERFETTO_DPLOG("kill");
+    PERFETTO_DLOG("Sending signal: %d (si_value: %d) to pid: %d",
+                  kProfilingSignal, kHeapprofdSignalValue, pid);
+    union sigval signal_value;
+    signal_value.sival_int = kHeapprofdSignalValue;
+    if (sigqueue(pid, kProfilingSignal, signal_value) != 0) {
+      PERFETTO_DPLOG("sigqueue");
     }
     ++pid_it;
   }
diff --git a/src/profiling/memory/heapprofd_producer_unittest.cc b/src/profiling/memory/heapprofd_producer_unittest.cc
index 66bd452..4e4b6ed 100644
--- a/src/profiling/memory/heapprofd_producer_unittest.cc
+++ b/src/profiling/memory/heapprofd_producer_unittest.cc
@@ -41,7 +41,7 @@
   MOCK_CONST_METHOD0(shared_buffer_page_size_kb, size_t());
   MOCK_METHOD2(CreateTraceWriter,
                std::unique_ptr<TraceWriter>(BufferID, BufferExhaustedPolicy));
-  MOCK_METHOD0(GetInProcessShmemArbiter, SharedMemoryArbiter*());
+  MOCK_METHOD0(MaybeSharedMemoryArbiter, SharedMemoryArbiter*());
   MOCK_METHOD1(ActivateTriggers, void(const std::vector<std::string>&));
 
   MOCK_METHOD1(RegisterDataSource, void(const DataSourceDescriptor&));
diff --git a/src/profiling/memory/proc_utils.cc b/src/profiling/memory/proc_utils.cc
index 02f2c7d..6a4c16b 100644
--- a/src/profiling/memory/proc_utils.cc
+++ b/src/profiling/memory/proc_utils.cc
@@ -76,7 +76,8 @@
     return false;
   }
   char cmdline[512];
-  ssize_t rd = read(*fd, cmdline, sizeof(cmdline) - 1);
+  const size_t max_read_size = sizeof(cmdline) - 1;
+  ssize_t rd = read(*fd, cmdline, max_read_size);
   if (rd == -1) {
     PERFETTO_DPLOG("Failed to read %s", filename.c_str());
     return false;
@@ -88,8 +89,12 @@
     return false;
   }
 
-  // We did not manage to read the first argument.
-  if (memchr(cmdline, '\0', static_cast<size_t>(rd)) == nullptr) {
+  // In some buggy kernels (before http://bit.ly/37R7qwL) /proc/pid/cmdline is
+  // not NUL-terminated (see b/147438623). If we read < max_read_size bytes
+  // assume we are hitting the aforementioned kernel bug and terminate anyways.
+  const size_t rd_u = static_cast<size_t>(rd);
+  if (rd_u >= max_read_size && memchr(cmdline, '\0', rd_u) == nullptr) {
+    // We did not manage to read the first argument.
     PERFETTO_DLOG("Overflow reading cmdline for %" PRIdMAX,
                   static_cast<intmax_t>(pid));
     errno = EOVERFLOW;
@@ -98,7 +103,7 @@
 
   cmdline[rd] = '\0';
   char* cmdline_start = cmdline;
-  ssize_t size = NormalizeCmdLine(&cmdline_start, static_cast<size_t>(rd));
+  ssize_t size = NormalizeCmdLine(&cmdline_start, rd_u);
   if (size == -1)
     return false;
   name->assign(cmdline_start, static_cast<size_t>(size));
diff --git a/src/profiling/memory/shared_ring_buffer.cc b/src/profiling/memory/shared_ring_buffer.cc
index 99a9c89..826425d 100644
--- a/src/profiling/memory/shared_ring_buffer.cc
+++ b/src/profiling/memory/shared_ring_buffer.cc
@@ -104,6 +104,18 @@
     size_t outer_size = kMetaPageSize + size_ * 2 + kGuardSize;
     munmap(meta_, outer_size);
   }
+
+  // This is work-around for code like the following:
+  // https://android.googlesource.com/platform/libcore/+/4ecb71f94378716f88703b9f7548b5d24839262f/ojluni/src/main/native/UNIXProcess_md.c#427
+  // They fork, close all fds by iterating over /proc/self/fd using opendir.
+  // Unfortunately closedir calls free, which detects the fork, and then tries
+  // to destruct the Client that holds this SharedRingBuffer.
+  //
+  // ScopedResource crashes on failure to close, so we explicitly ignore
+  // failures here.
+  int fd = mem_fd_.release();
+  if (fd != -1)
+    close(fd);
 }
 
 void SharedRingBuffer::Initialize(base::ScopedFile mem_fd) {
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index 6b030c0..339077b 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -58,6 +58,8 @@
 namespace profiling {
 namespace {
 
+constexpr base::TimeMillis kMapsReparseInterval{500};
+
 constexpr size_t kMaxFrames = 1000;
 
 // We assume average ~300us per unwind. If we handle up to 1000 unwinds, this
@@ -155,6 +157,9 @@
   std::string content;
   if (!base::ReadFileDescriptor(*fd_, &content))
     return false;
+
+  unwindstack::MapInfo* prev_map = nullptr;
+  unwindstack::MapInfo* prev_real_map = nullptr;
   return android::procinfo::ReadMapFileContent(
       &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
                        uint64_t pgoff, ino_t, const char* name) {
@@ -163,10 +168,12 @@
             strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        unwindstack::MapInfo* prev_map =
-            maps_.empty() ? nullptr : maps_.back().get();
         maps_.emplace_back(
-            new unwindstack::MapInfo(prev_map, start, end, pgoff, flags, name));
+            new unwindstack::MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+        prev_map = maps_.back().get();
+        if (!prev_map->IsBlank()) {
+          prev_real_map = prev_map;
+        }
       });
 }
 
@@ -204,8 +211,14 @@
   uint8_t error_code = 0;
   for (int attempt = 0; attempt < 2; ++attempt) {
     if (attempt > 0) {
+      if (metadata->last_maps_reparse_time + kMapsReparseInterval >
+          base::GetWallTimeMs()) {
+        PERFETTO_DLOG("Skipping reparse due to rate limit.");
+        break;
+      }
       PERFETTO_DLOG("Reparsing maps");
       metadata->ReparseMaps();
+      metadata->last_maps_reparse_time = base::GetWallTimeMs();
       // Regs got invalidated by libuwindstack's speculative jump.
       // Reset.
       ReadFromRawData(regs.get(), alloc_metadata->register_data);
diff --git a/src/profiling/memory/unwinding.h b/src/profiling/memory/unwinding.h
index 39216fe..a702c64 100644
--- a/src/profiling/memory/unwinding.h
+++ b/src/profiling/memory/unwinding.h
@@ -27,6 +27,7 @@
 #include <unwindstack/JitDebug.h>
 #endif
 
+#include "perfetto/base/time.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
@@ -129,6 +130,7 @@
   // The API of libunwindstack expects shared_ptr for Memory.
   std::shared_ptr<unwindstack::Memory> fd_mem;
   uint64_t reparses = 0;
+  base::TimeMillis last_maps_reparse_time{0};
 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
   std::unique_ptr<unwindstack::JitDebug> jit_debug;
   std::unique_ptr<unwindstack::DexFiles> dex_files;
diff --git a/src/profiling/perf/BUILD.gn b/src/profiling/perf/BUILD.gn
index b864674..80b0cb8 100644
--- a/src/profiling/perf/BUILD.gn
+++ b/src/profiling/perf/BUILD.gn
@@ -18,17 +18,12 @@
 
 assert(enable_perfetto_traced_perf)
 
-# TODO(rsavitski): only building in-tree at the moment (so this build file is
-# only used for gen_android_bp, expect bitrot).
-
 executable("traced_perf") {
   deps = [
     ":traced_perf_main",
     "../../../gn:default_deps",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
 }
 
 source_set("traced_perf_main") {
@@ -36,7 +31,7 @@
     ":producer",
     "../../../gn:default_deps",
     "../../../src/base",
-    "../../../src/tracing:ipc",
+    "../../../src/tracing/ipc/producer",
   ]
   sources = [
     "traced_perf.cc",
@@ -45,18 +40,18 @@
 }
 
 source_set("producer") {
-  deps = [
+  public_deps = [
     ":unwind_support",
+    "../../../include/perfetto/tracing/core",
+  ]
+  deps = [
     "../../../gn:default_deps",
     "../../../protos/perfetto/config:cpp",
     "../../../protos/perfetto/config/profiling:zero",
     "../../../protos/perfetto/trace:zero",
     "../../../src/base",
     "../../../src/base:unix_socket",
-    "../../../src/tracing:ipc",
-  ]
-  public_deps = [
-    "../../../include/perfetto/tracing/core",
+    "../../../src/tracing/ipc/producer",
   ]
   sources = [
     "event_config.h",
@@ -68,9 +63,10 @@
 }
 
 source_set("unwind_support") {
+  public_deps = [ "../../../gn:libunwindstack" ]
   deps = [
+    "../../../gn:bionic_kernel_uapi_headers",
     "../../../gn:default_deps",
-    "../../../gn:libunwindstack",
     "../../../src/base",
   ]
   sources = [
@@ -91,7 +87,5 @@
     "../../../src/protozero",
     "../../base",
   ]
-  sources = [
-    "event_config_unittest.cc",
-  ]
+  sources = [ "event_config_unittest.cc" ]
 }
diff --git a/src/profiling/perf/event_reader.cc b/src/profiling/perf/event_reader.cc
index a2d0e87..f7b297f 100644
--- a/src/profiling/perf/event_reader.cc
+++ b/src/profiling/perf/event_reader.cc
@@ -56,7 +56,8 @@
 // cpu-scoped?
 base::ScopedFile PerfEventOpen(const EventConfig& event_cfg) {
   base::ScopedFile perf_fd{
-      perf_event_open(event_cfg.perf_attr(), /*pid=*/-1, event_cfg.target_cpu(),
+      perf_event_open(event_cfg.perf_attr(), /*pid=*/-1,
+                      static_cast<int>(event_cfg.target_cpu()),
                       /*group_fd=*/-1, PERF_FLAG_FD_CLOEXEC)};
   return perf_fd;
 }
diff --git a/src/profiling/perf/unwind_support.cc b/src/profiling/perf/unwind_support.cc
index 47dc3c0..a3cef1b 100644
--- a/src/profiling/perf/unwind_support.cc
+++ b/src/profiling/perf/unwind_support.cc
@@ -28,13 +28,13 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/UserArm.h>
 #include <unwindstack/UserArm64.h>
+#include <unwindstack/UserX86.h>
+#include <unwindstack/UserX86_64.h>
 
-// TODO(rsavitski): this includes the kernel uapi constant definitions (for
-// register sampling). For now hardcoded for in-tree builds (specifically,
-// bionic/include/kernel/). Standalone builds will need to source the headers
-// from elsewhere (without depending on the host machine's system headers).
 #include <uapi/asm-arm/asm/perf_regs.h>
 #include <uapi/asm-x86/asm/perf_regs.h>
 #define perf_event_arm_regs perf_event_arm64_regs
@@ -46,6 +46,10 @@
 
 namespace {
 
+constexpr size_t constexpr_max(size_t x, size_t y) {
+  return x > y ? x : y;
+}
+
 template <typename T>
 const char* ReadValue(T* value_out, const char* ptr) {
   memcpy(value_out, reinterpret_cast<const void*>(ptr), sizeof(T));
@@ -57,17 +61,30 @@
 // * 64 bit daemon, mixed bitness userspace
 // Therefore give the kernel the mask corresponding to our build architecture.
 // Register parsing handles the mixed userspace ABI cases.
+// For simplicity, we ask for as many registers as we can, even if not all of
+// them will be used during unwinding.
 // TODO(rsavitski): cleanly detect 32 bit builds being side-loaded onto a system
 // with 64 bit userspace processes.
 uint64_t PerfUserRegsMask(unwindstack::ArchEnum arch) {
-  // TODO(rsavitski): support the rest of the architectures.
-  switch (arch) {
+  switch (static_cast<uint8_t>(arch)) {  // cast to please -Wswitch-enum
     case unwindstack::ARCH_ARM64:
       return (1ULL << PERF_REG_ARM64_MAX) - 1;
     case unwindstack::ARCH_ARM:
       return ((1ULL << PERF_REG_ARM_MAX) - 1);
+    // perf on x86_64 doesn't allow sampling ds/es/fs/gs registers. See
+    // arch/x86/kernel/perf_regs.c in the kernel.
+    case unwindstack::ARCH_X86_64:
+      return (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~(1ULL << PERF_REG_X86_DS) &
+              ~(1ULL << PERF_REG_X86_ES) & ~(1ULL << PERF_REG_X86_FS) &
+              ~(1ULL << PERF_REG_X86_GS));
+    // Note: excluding these segment registers might not be necessary on x86,
+    // but they won't be used anyway (so follow x64).
+    case unwindstack::ARCH_X86:
+      return ((1ULL << PERF_REG_X86_32_MAX) - 1) & ~(1ULL << PERF_REG_X86_DS) &
+             ~(1ULL << PERF_REG_X86_ES) & ~(1ULL << PERF_REG_X86_FS) &
+             ~(1ULL << PERF_REG_X86_GS);
     default:
-      PERFETTO_FATAL("Unsupported architecture (work in progress)");
+      PERFETTO_FATAL("Unsupported architecture");
   }
 }
 
@@ -86,59 +103,100 @@
 
 // Register values as an array, indexed using the kernel uapi perf_events.h enum
 // values. Unsampled values will be left as zeroes.
-// TODO(rsavitski): support all relevant architectures (allocate enough space
-// for the widest register bank).
 struct RawRegisterData {
-  static constexpr uint64_t kMaxSize = PERF_REG_ARM64_MAX;
+  static constexpr uint64_t kMaxSize =
+      constexpr_max(PERF_REG_ARM64_MAX,
+                    constexpr_max(PERF_REG_ARM_MAX, PERF_REG_X86_64_MAX));
   uint64_t regs[kMaxSize] = {};
 };
 
+// First converts the |RawRegisterData| array to libunwindstack's "user"
+// register structs (which match the ptrace/coredump format, also available at
+// <sys/user.h>), then constructs the relevant unwindstack::Regs subclass out
+// of the latter.
 std::unique_ptr<unwindstack::Regs> ToLibUnwindstackRegs(
     const RawRegisterData& raw_regs,
     unwindstack::ArchEnum arch) {
-  // First converts the |RawRegisterData| array to libunwindstack's raw register
-  // format, then constructs the relevant unwindstack::Regs subclass out of the
-  // latter.
   if (arch == unwindstack::ARCH_ARM64) {
     static_assert(static_cast<int>(unwindstack::ARM64_REG_R0) ==
-                      static_cast<int>(PERF_REG_ARM64_X0),
+                          static_cast<int>(PERF_REG_ARM64_X0) &&
+                      static_cast<int>(unwindstack::ARM64_REG_R0) == 0,
                   "register layout mismatch");
     static_assert(static_cast<int>(unwindstack::ARM64_REG_R30) ==
                       static_cast<int>(PERF_REG_ARM64_LR),
                   "register layout mismatch");
-
-    unwindstack::arm64_user_regs arm64_user_regs;
-    memset(&arm64_user_regs, 0, sizeof(arm64_user_regs));
-    memcpy(&arm64_user_regs.regs[unwindstack::ARM64_REG_R0],
-           &raw_regs.regs[PERF_REG_ARM64_X0],
-           sizeof(uint64_t) * (PERF_REG_ARM64_LR - PERF_REG_ARM64_X0 + 1));
+    // Both the perf_event register order and the "user" format are derived from
+    // "struct pt_regs", so we can directly memcpy the first 31 regs (up to and
+    // including LR).
+    unwindstack::arm64_user_regs arm64_user_regs = {};
+    memcpy(&arm64_user_regs.regs[0], &raw_regs.regs[0],
+           sizeof(uint64_t) * (PERF_REG_ARM64_LR + 1));
     arm64_user_regs.sp = raw_regs.regs[PERF_REG_ARM64_SP];
     arm64_user_regs.pc = raw_regs.regs[PERF_REG_ARM64_PC];
-
     return std::unique_ptr<unwindstack::Regs>(
         unwindstack::RegsArm64::Read(&arm64_user_regs));
   }
 
   if (arch == unwindstack::ARCH_ARM) {
     static_assert(static_cast<int>(unwindstack::ARM_REG_R0) ==
-                      static_cast<int>(PERF_REG_ARM_R0),
+                          static_cast<int>(PERF_REG_ARM_R0) &&
+                      static_cast<int>(unwindstack::ARM_REG_R0) == 0,
                   "register layout mismatch");
     static_assert(static_cast<int>(unwindstack::ARM_REG_LAST) ==
                       static_cast<int>(PERF_REG_ARM_MAX),
                   "register layout mismatch");
-
-    unwindstack::arm_user_regs arm_user_regs;
-    memset(&arm_user_regs, 0, sizeof(arm_user_regs));
-    for (size_t i = unwindstack::ARM_REG_R0; i < unwindstack::ARM_REG_LAST;
-         i++) {
+    // As with arm64, the layouts match, but we need to downcast to u32.
+    unwindstack::arm_user_regs arm_user_regs = {};
+    for (size_t i = 0; i < unwindstack::ARM_REG_LAST; i++) {
       arm_user_regs.regs[i] = static_cast<uint32_t>(raw_regs.regs[i]);
     }
-
     return std::unique_ptr<unwindstack::Regs>(
         unwindstack::RegsArm::Read(&arm_user_regs));
   }
 
-  PERFETTO_FATAL("Unsupported architecture (work in progress)");
+  if (arch == unwindstack::ARCH_X86_64) {
+    // We've sampled more registers than what libunwindstack will use. Don't
+    // copy over cs/ss/flags.
+    unwindstack::x86_64_user_regs x86_64_user_regs = {};
+    x86_64_user_regs.rax = raw_regs.regs[PERF_REG_X86_AX];
+    x86_64_user_regs.rbx = raw_regs.regs[PERF_REG_X86_BX];
+    x86_64_user_regs.rcx = raw_regs.regs[PERF_REG_X86_CX];
+    x86_64_user_regs.rdx = raw_regs.regs[PERF_REG_X86_DX];
+    x86_64_user_regs.r8 = raw_regs.regs[PERF_REG_X86_R8];
+    x86_64_user_regs.r9 = raw_regs.regs[PERF_REG_X86_R9];
+    x86_64_user_regs.r10 = raw_regs.regs[PERF_REG_X86_R10];
+    x86_64_user_regs.r11 = raw_regs.regs[PERF_REG_X86_R11];
+    x86_64_user_regs.r12 = raw_regs.regs[PERF_REG_X86_R12];
+    x86_64_user_regs.r13 = raw_regs.regs[PERF_REG_X86_R13];
+    x86_64_user_regs.r14 = raw_regs.regs[PERF_REG_X86_R14];
+    x86_64_user_regs.r15 = raw_regs.regs[PERF_REG_X86_R15];
+    x86_64_user_regs.rdi = raw_regs.regs[PERF_REG_X86_DI];
+    x86_64_user_regs.rsi = raw_regs.regs[PERF_REG_X86_SI];
+    x86_64_user_regs.rbp = raw_regs.regs[PERF_REG_X86_BP];
+    x86_64_user_regs.rsp = raw_regs.regs[PERF_REG_X86_SP];
+    x86_64_user_regs.rip = raw_regs.regs[PERF_REG_X86_IP];
+    return std::unique_ptr<unwindstack::Regs>(
+        unwindstack::RegsX86_64::Read(&x86_64_user_regs));
+  }
+
+  if (arch == unwindstack::ARCH_X86) {
+    // We've sampled more registers than what libunwindstack will use. Don't
+    // copy over cs/ss/flags.
+    unwindstack::x86_user_regs x86_user_regs = {};
+    x86_user_regs.eax = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_AX]);
+    x86_user_regs.ebx = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_BX]);
+    x86_user_regs.ecx = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_CX]);
+    x86_user_regs.edx = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_DX]);
+    x86_user_regs.ebp = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_BP]);
+    x86_user_regs.edi = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_DI]);
+    x86_user_regs.esi = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_SI]);
+    x86_user_regs.esp = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_SP]);
+    x86_user_regs.eip = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_IP]);
+    return std::unique_ptr<unwindstack::Regs>(
+        unwindstack::RegsX86::Read(&x86_user_regs));
+  }
+
+  PERFETTO_FATAL("Unsupported architecture");
 }
 
 }  // namespace
@@ -164,7 +222,7 @@
   RawRegisterData raw_regs{};
   uint64_t regs_mask = PerfUserRegsMaskForCurrentArch();
   for (size_t i = 0; regs_mask && (i < RawRegisterData::kMaxSize); i++) {
-    if (regs_mask & (1u << i)) {
+    if (regs_mask & (1ULL << i)) {
       parse_pos = ReadValue(&raw_regs.regs[i], parse_pos);
     }
   }
@@ -178,6 +236,7 @@
   // the PC into the R15 slot, and treat the resulting RawRegisterData as an
   // arm32 register bank. See "Fundamentals of ARMv8-A" (ARM DOC
   // 100878_0100_en), page 28.
+  // x86-64 doesn't need any such fixups.
   if (requested_arch == unwindstack::ARCH_ARM64 &&
       sampled_abi == PERF_SAMPLE_REGS_ABI_32) {
     raw_regs.regs[PERF_REG_ARM_PC] = raw_regs.regs[PERF_REG_ARM64_PC];
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
index 3bbc385..69c9446 100644
--- a/src/profiling/symbolizer/BUILD.gn
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -15,12 +15,8 @@
 import("../../../gn/perfetto.gni")
 
 source_set("symbolizer") {
-  public_deps = [
-    "../../../include/perfetto/ext/base",
-  ]
-  deps = [
-    "../../../gn:default_deps",
-  ]
+  public_deps = [ "../../../include/perfetto/ext/base" ]
+  deps = [ "../../../gn:default_deps" ]
   sources = [
     "local_symbolizer.cc",
     "local_symbolizer.h",
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index e4cbd82..124bb56 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -43,9 +43,7 @@
 
 static_library("libprotozero") {
   complete_static_lib = true
-  deps = [
-    ":protozero",
-  ]
+  deps = [ ":protozero" ]
 }
 
 perfetto_unittest_source_set("unittests") {
@@ -87,9 +85,7 @@
 }
 
 perfetto_fuzzer_test("protozero_decoder_fuzzer") {
-  sources = [
-    "proto_decoder_fuzzer.cc",
-  ]
+  sources = [ "proto_decoder_fuzzer.cc" ]
   deps = [
     ":protozero",
     "../../gn:default_deps",
diff --git a/src/protozero/protoc_plugin/BUILD.gn b/src/protozero/protoc_plugin/BUILD.gn
index 4a9c7f4..a628be7 100644
--- a/src/protozero/protoc_plugin/BUILD.gn
+++ b/src/protozero/protoc_plugin/BUILD.gn
@@ -17,9 +17,7 @@
 # The plugin that generates zero-copy serializers and deserializers. Those are
 # the xxx.pbzero.h headers used all over the codebase.
 perfetto_host_executable("protozero_plugin") {
-  sources = [
-    "protozero_plugin.cc",
-  ]
+  sources = [ "protozero_plugin.cc" ]
   deps = [
     "../../../gn:default_deps",
     "../../../gn:protoc_lib",
@@ -31,9 +29,7 @@
 # This is used for core classes traced needs to know about such as
 # DataSourceDescriptor.
 perfetto_host_executable("cppgen_plugin") {
-  sources = [
-    "cppgen_plugin.cc",
-  ]
+  sources = [ "cppgen_plugin.cc" ]
   deps = [
     "../../../gn:default_deps",
     "../../../gn:protoc_lib",
diff --git a/src/protozero/test/example_proto/library.proto b/src/protozero/test/example_proto/library.proto
index 059080f..7ca4b17 100644
--- a/src/protozero/test/example_proto/library.proto
+++ b/src/protozero/test/example_proto/library.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package protozero.test.protos;
 
diff --git a/src/protozero/test/example_proto/library_internals/galaxies.proto b/src/protozero/test/example_proto/library_internals/galaxies.proto
index d2a1e42..75b156a 100644
--- a/src/protozero/test/example_proto/library_internals/galaxies.proto
+++ b/src/protozero/test/example_proto/library_internals/galaxies.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package protozero.test.protos;
 
diff --git a/src/protozero/test/example_proto/test_messages.descriptor.h b/src/protozero/test/example_proto/test_messages.descriptor.h
index 9c2bcfa..f27e4ec 100644
--- a/src/protozero/test/example_proto/test_messages.descriptor.h
+++ b/src/protozero/test/example_proto/test_messages.descriptor.h
@@ -25,17 +25,17 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// f242f1ac484bbe7ba4c45e77b56ab588f8015196
+// d6628b15181dba5287e35b56b966b39ea93d42b1
 // SHA1(src/protozero/test/example_proto/test_messages.proto)
-// 61c0771c4c18c994996335be97413d944ce8f80d
+// bd59c7719b091c8277d2dce867d28dfa5b40bab2
 
 // This is the proto TestMessages encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 4181> kTestMessagesDescriptor{
-    {0x0a, 0x66, 0x0a, 0x33, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74,
+constexpr std::array<uint8_t, 4165> kTestMessagesDescriptor{
+    {0x0a, 0x62, 0x0a, 0x33, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74,
      0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x65,
      0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
      0x2f, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72,
@@ -43,61 +43,60 @@
      0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e,
      0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x54, 0x72,
      0x69, 0x63, 0x6b, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6d,
-     0x70, 0x6f, 0x72, 0x74, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xcc, 0x01, 0x0a,
-     0x41, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65,
-     0x72, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d,
-     0x70, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x69,
-     0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
-     0x61, 0x6c, 0x73, 0x2f, 0x67, 0x61, 0x6c, 0x61, 0x78, 0x69, 0x65, 0x73,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x33, 0x73, 0x72, 0x63, 0x2f, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74, 0x65, 0x73,
-     0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x69, 0x6d,
-     0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x35,
-     0x0a, 0x06, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x12, 0x0d, 0x0a, 0x09,
-     0x4d, 0x49, 0x4c, 0x4b, 0x59, 0x5f, 0x57, 0x41, 0x59, 0x10, 0x01, 0x12,
-     0x0d, 0x0a, 0x09, 0x41, 0x4e, 0x44, 0x52, 0x4f, 0x4d, 0x45, 0x44, 0x41,
-     0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x4e, 0x46, 0x4c, 0x4f,
-     0x57, 0x45, 0x52, 0x10, 0x03, 0x42, 0x02, 0x48, 0x03, 0x50, 0x00, 0x0a,
-     0xb4, 0x03, 0x0a, 0x2e, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x65,
-     0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+     0x70, 0x6f, 0x72, 0x74, 0x0a, 0xc8, 0x01, 0x0a, 0x41, 0x73, 0x72, 0x63,
+     0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74,
+     0x65, 0x73, 0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72,
+     0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x2f,
+     0x67, 0x61, 0x6c, 0x61, 0x78, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f,
      0x74, 0x6f, 0x12, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72,
      0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x1a, 0x41, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x1a, 0x33, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
      0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x65, 0x78,
      0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
-     0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65,
-     0x72, 0x6e, 0x61, 0x6c, 0x73, 0x2f, 0x67, 0x61, 0x6c, 0x61, 0x78, 0x69,
-     0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa1, 0x02, 0x0a,
-     0x14, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x67, 0x61, 0x6c, 0x61, 0x63, 0x74,
-     0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x42, 0x0a,
-     0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x67, 0x61, 0x6c, 0x61,
-     0x78, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x74, 0x65, 0x73,
-     0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x61, 0x6c,
-     0x61, 0x78, 0x79, 0x52, 0x0c, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x47,
-     0x61, 0x6c, 0x61, 0x78, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x72, 0x69,
-     0x67, 0x69, 0x6e, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
-     0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x12, 0x4c, 0x0a, 0x12, 0x64, 0x65,
-     0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x61,
-     0x6c, 0x61, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x74,
-     0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47,
-     0x61, 0x6c, 0x61, 0x78, 0x79, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69,
-     0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79,
-     0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x18, 0x04,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x12,
-     0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x73,
-     0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-     0x42, 0x02, 0x48, 0x03, 0x50, 0x00, 0x0a, 0xe4, 0x1a, 0x0a, 0x34, 0x73,
+     0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x35, 0x0a, 0x06, 0x47, 0x61,
+     0x6c, 0x61, 0x78, 0x79, 0x12, 0x0d, 0x0a, 0x09, 0x4d, 0x49, 0x4c, 0x4b,
+     0x59, 0x5f, 0x57, 0x41, 0x59, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x41,
+     0x4e, 0x44, 0x52, 0x4f, 0x4d, 0x45, 0x44, 0x41, 0x10, 0x02, 0x12, 0x0d,
+     0x0a, 0x09, 0x53, 0x55, 0x4e, 0x46, 0x4c, 0x4f, 0x57, 0x45, 0x52, 0x10,
+     0x03, 0x50, 0x00, 0x0a, 0xb0, 0x03, 0x0a, 0x2e, 0x73, 0x72, 0x63, 0x2f,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74, 0x65,
+     0x73, 0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x41, 0x73, 0x72, 0x63, 0x2f, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x74, 0x65, 0x73,
+     0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f,
+     0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x2f, 0x67, 0x61,
+     0x6c, 0x61, 0x78, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x22, 0xa1, 0x02, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x67, 0x61,
+     0x6c, 0x61, 0x63, 0x74, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
+     0x65, 0x12, 0x42, 0x0a, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f,
+     0x67, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
+     0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f,
+     0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x52, 0x0c, 0x6f, 0x72, 0x69,
+     0x67, 0x69, 0x6e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x12, 0x23, 0x0a,
+     0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x70, 0x6c, 0x61, 0x6e,
+     0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x72,
+     0x69, 0x67, 0x69, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x12, 0x4c,
+     0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x5f, 0x67, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65,
+     0x72, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x52, 0x11, 0x64,
+     0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x61,
+     0x6c, 0x61, 0x78, 0x79, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74,
+     0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6c, 0x61, 0x6e,
+     0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65,
+     0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6c, 0x61,
+     0x6e, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01,
+     0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x65, 0x73,
+     0x73, 0x61, 0x67, 0x65, 0x50, 0x00, 0x0a, 0xe0, 0x1a, 0x0a, 0x34, 0x73,
      0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x7a, 0x65, 0x72, 0x6f,
      0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
      0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74,
@@ -224,19 +223,19 @@
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x42,
      0x61, 0x7a, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, 0x61,
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x6f, 0x6f, 0x4d,
-     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 0x6f,
+     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x4d, 0x6f,
      0x6f, 0x4d, 0x6f, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x52, 0x4c, 0x45,
      0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x0a, 0x75, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+     0x52, 0x0a, 0x55, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
      0x12, 0x12, 0x0a, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x04, 0x78, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
+     0x28, 0x08, 0x52, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
      0x55, 0x72, 0x4c, 0x45, 0x5f, 0x6e, 0x63, 0x6f, 0x5f, 0x5f, 0x64, 0x65,
-     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x72, 0x4c,
+     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x72, 0x4c,
      0x45, 0x4e, 0x63, 0x6f, 0x44, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x09, 0x5f,
      0x5f, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x07, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
+     0x28, 0x08, 0x52, 0x07, 0x42, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
      0x0e, 0x0a, 0x02, 0x55, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x02, 0x75, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
+     0x02, 0x55, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
      0x69, 0x67, 0x5f, 0x5f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
      0x62, 0x61, 0x6e, 0x67, 0x42, 0x69, 0x67, 0x22, 0x8f, 0x01, 0x0a, 0x14,
      0x50, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74,
@@ -383,7 +382,7 @@
      0xff, 0xff, 0xff, 0x01, 0x2a, 0x1f, 0x0a, 0x07, 0x42, 0x69, 0x67, 0x45,
      0x6e, 0x75, 0x6d, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x45, 0x47, 0x49, 0x4e,
      0x10, 0x0a, 0x12, 0x09, 0x0a, 0x03, 0x45, 0x4e, 0x44, 0x10, 0x94, 0x91,
-     0x06, 0x42, 0x02, 0x48, 0x03}};
+     0x06}};
 
 }  // namespace perfetto
 
diff --git a/src/protozero/test/example_proto/test_messages.proto b/src/protozero/test/example_proto/test_messages.proto
index 298b77f..f8f2d31 100644
--- a/src/protozero/test/example_proto/test_messages.proto
+++ b/src/protozero/test/example_proto/test_messages.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package protozero.test.protos;
 
diff --git a/src/protozero/test/example_proto/upper_import.proto b/src/protozero/test/example_proto/upper_import.proto
index 486eff9..4479c25 100644
--- a/src/protozero/test/example_proto/upper_import.proto
+++ b/src/protozero/test/example_proto/upper_import.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package protozero.test.protos;
 
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index a6fd002..c057068 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -23,11 +23,11 @@
 
 # The library which eases processing of Perfetto traces by exposing reading
 # friendly APIs.
-static_library("trace_processor") {
-  complete_static_lib = true
-  deps = [
-    ":lib",
-  ]
+if (enable_perfetto_trace_processor_sqlite) {
+  static_library("trace_processor") {
+    complete_static_lib = true
+    deps = [ ":lib" ]
+  }
 }
 
 if (enable_perfetto_ui) {
@@ -86,8 +86,8 @@
     "forwarding_trace_parser.cc",
     "forwarding_trace_parser.h",
     "ftrace_utils.h",
-    "gzip_trace_parser.cc",
-    "gzip_trace_parser.h",
+    "global_args_tracker.cc",
+    "global_args_tracker.h",
     "heap_profile_tracker.cc",
     "heap_profile_tracker.h",
     "importers/ftrace/ftrace_module.cc",
@@ -136,18 +136,16 @@
     "trace_storage.h",
     "track_tracker.cc",
     "track_tracker.h",
-    "variadic.h",
     "virtual_destructors.cc",
   ]
-
   deps = [
     ":descriptors",
     "../../gn:default_deps",
-    "../../gn:zlib",
     "../base",
     "../protozero",
     "containers",
     "tables",
+    "types",
   ]
   public_deps = [
     "../../include/perfetto/trace_processor:storage",
@@ -165,6 +163,13 @@
     "../../protos/perfetto/trace/sys_stats:zero",
     "../../protos/perfetto/trace/track_event:zero",
   ]
+  if (enable_perfetto_zlib) {
+    sources += [
+      "gzip_trace_parser.cc",
+      "gzip_trace_parser.h",
+    ]
+    deps += [ "../../gn:zlib" ]
+  }
   if (enable_perfetto_trace_processor_json_import) {
     sources += [
       "importers/json/json_trace_parser.cc",
@@ -238,9 +243,7 @@
     "syscalls_armeabi.h",
     "syscalls_x86_64.h",
   ]
-  public_deps = [
-    ":storage_minimal",
-  ]
+  public_deps = [ ":storage_minimal" ]
   deps = [
     "../../include/perfetto/ext/base:base",
     "../../include/perfetto/ext/traced:sys_stats_counters",
@@ -250,6 +253,7 @@
     "../../protos/perfetto/trace/gpu:zero",
     "../../protos/perfetto/trace/interned_data:zero",
     "../protozero",
+    "types",
   ]
   if (enable_perfetto_trace_processor_json) {
     public_deps += [ "../../gn:jsoncpp" ]
@@ -274,129 +278,116 @@
       "../../gn:jsoncpp",
       "../base",
     ]
-    public_deps = [
-      "../../include/perfetto/ext/trace_processor:export_json",
+    public_deps = [ "../../include/perfetto/ext/trace_processor:export_json" ]
+  }
+}
+
+if (enable_perfetto_trace_processor_sqlite) {
+  source_set("lib") {
+    sources = [
+      "filtered_row_index.cc",
+      "filtered_row_index.h",
+      "read_trace.cc",
+      "row_iterators.cc",
+      "row_iterators.h",
+      "sched_slice_table.cc",
+      "sched_slice_table.h",
+      "span_join_operator_table.cc",
+      "span_join_operator_table.h",
+      "sql_stats_table.cc",
+      "sql_stats_table.h",
+      "sqlite_experimental_flamegraph_table.cc",
+      "sqlite_experimental_flamegraph_table.h",
+      "sqlite_raw_table.cc",
+      "sqlite_raw_table.h",
+      "stats_table.cc",
+      "stats_table.h",
+      "storage_columns.cc",
+      "storage_columns.h",
+      "storage_schema.cc",
+      "storage_schema.h",
+      "storage_table.cc",
+      "storage_table.h",
+      "trace_processor.cc",
+      "trace_processor_impl.cc",
+      "trace_processor_impl.h",
+      "window_operator_table.cc",
+      "window_operator_table.h",
+    ]
+
+    deps = [
+      ":storage_full",
+      "../../gn:default_deps",
+      "../../gn:sqlite",
+      "../../protos/perfetto/metrics:zero",
+      "../../protos/perfetto/metrics/android:zero",
+      "../../protos/perfetto/trace/ftrace:zero",
+      "../base",
+      "db:lib",
+      "metrics:lib",
+      "sqlite",
+      "tables",
+      "types",
+    ]
+    public_deps = [ "../../include/perfetto/trace_processor" ]
+    if (enable_perfetto_trace_processor_json) {
+      deps += [ ":export_json" ]
+    }
+  }
+
+  perfetto_host_executable("trace_processor_shell") {
+    deps = [
+      ":lib",
+      "../../gn:default_deps",
+      "../../gn:protoc_lib",
+      "../../src/profiling/symbolizer",
+      "../../src/profiling/symbolizer:symbolize_database",
+      "../base",
+      "metrics:lib",
+    ]
+    if (enable_perfetto_version_gen) {
+      deps += [ "../../gn/standalone:gen_git_revision" ]
+    }
+    if (enable_perfetto_trace_processor_linenoise) {
+      deps += [ "../../gn:linenoise" ]
+    }
+    if (enable_perfetto_trace_processor_httpd) {
+      deps += [ "rpc:httpd" ]
+    }
+    sources = [
+      "proto_to_json.cc",
+      "proto_to_json.h",
+      "trace_processor_shell.cc",
     ]
   }
-}
-
-source_set("lib") {
-  sources = [
-    "args_table.cc",
-    "args_table.h",
-    "filtered_row_index.cc",
-    "filtered_row_index.h",
-    "gfp_flags.cc",
-    "gfp_flags.h",
-    "process_table.cc",
-    "process_table.h",
-    "raw_table.cc",
-    "raw_table.h",
-    "read_trace.cc",
-    "row_iterators.cc",
-    "row_iterators.h",
-    "sched_slice_table.cc",
-    "sched_slice_table.h",
-    "span_join_operator_table.cc",
-    "span_join_operator_table.h",
-    "sql_stats_table.cc",
-    "sql_stats_table.h",
-    "stats_table.cc",
-    "stats_table.h",
-    "storage_columns.cc",
-    "storage_columns.h",
-    "storage_schema.cc",
-    "storage_schema.h",
-    "storage_table.cc",
-    "storage_table.h",
-    "thread_table.cc",
-    "thread_table.h",
-    "trace_processor.cc",
-    "trace_processor_impl.cc",
-    "trace_processor_impl.h",
-    "window_operator_table.cc",
-    "window_operator_table.h",
-  ]
-
-  deps = [
-    ":storage_full",
-    "../../gn:default_deps",
-    "../../gn:sqlite",
-    "../../protos/perfetto/metrics:zero",
-    "../../protos/perfetto/metrics/android:zero",
-    "../../protos/perfetto/trace/ftrace:zero",
-    "../base",
-    "db:lib",
-    "metrics:lib",
-    "sqlite",
-    "tables",
-  ]
-  public_deps = [
-    "../../include/perfetto/trace_processor",
-  ]
-  if (enable_perfetto_trace_processor_json) {
-    deps += [ ":export_json" ]
-  }
-}
-
-perfetto_host_executable("trace_processor_shell") {
-  deps = [
-    ":lib",
-    "../../gn:default_deps",
-    "../../gn:protoc_lib",
-    "../../src/profiling/symbolizer",
-    "../../src/profiling/symbolizer:symbolize_database",
-    "../base",
-    "metrics:lib",
-  ]
-  if (enable_perfetto_version_gen) {
-    deps += [ "../../gn/standalone:gen_git_revision" ]
-  }
-  if (enable_perfetto_trace_processor_linenoise) {
-    deps += [ "../../gn:linenoise" ]
-  }
-  if (enable_perfetto_trace_processor_httpd) {
-    deps += [ "rpc:httpd" ]
-  }
-  sources = [
-    "proto_to_json.cc",
-    "proto_to_json.h",
-    "trace_processor_shell.cc",
-  ]
-}
+}  # if (enable_perfetto_trace_processor_sqlite)
 
 perfetto_unittest_source_set("unittests") {
   testonly = true
   sources = [
-    "args_table_unittest.cc",
     "clock_tracker_unittest.cc",
     "event_tracker_unittest.cc",
-    "filtered_row_index_unittest.cc",
     "forwarding_trace_parser_unittest.cc",
     "ftrace_utils_unittest.cc",
     "heap_profile_tracker_unittest.cc",
     "importers/proto/args_table_utils_unittest.cc",
+    "importers/proto/heap_graph_tracker_unittest.cc",
     "importers/proto/heap_graph_walker_unittest.cc",
     "importers/proto/proto_trace_parser_unittest.cc",
     "importers/systrace/systrace_parser_unittest.cc",
-    "process_table_unittest.cc",
     "process_tracker_unittest.cc",
     "protozero_to_text_unittests.cc",
     "sched_slice_table_unittest.cc",
     "slice_tracker_unittest.cc",
-    "span_join_operator_table_unittest.cc",
     "syscall_tracker_unittest.cc",
-    "thread_table_unittest.cc",
     "trace_sorter_unittest.cc",
   ]
   deps = [
     ":descriptors",
-    ":lib",
     ":protozero_to_text",
     ":storage_full",
     "../../gn:default_deps",
     "../../gn:gtest_and_gmock",
-    "../../gn:sqlite",
     "../../protos/perfetto/common:zero",
     "../../protos/perfetto/trace:minimal_zero",
     "../../protos/perfetto/trace:zero",
@@ -414,11 +405,22 @@
     "../protozero:testing_messages_zero",
     "containers:unittests",
     "db:unittests",
-    "sqlite",
-    "sqlite:unittests",
     "tables:unittests",
   ]
 
+  if (enable_perfetto_trace_processor_sqlite) {
+    sources += [
+      "filtered_row_index_unittest.cc",
+      "span_join_operator_table_unittest.cc",
+    ]
+    deps += [
+      ":lib",
+      "../../gn:sqlite",
+      "sqlite",
+      "sqlite:unittests",
+    ]
+  }
+
   if (enable_perfetto_trace_processor_json) {
     if (enable_perfetto_trace_processor_json_import) {
       sources += [
@@ -445,30 +447,29 @@
 
 source_set("integrationtests") {
   testonly = true
-  sources = [
-    "trace_database_integrationtest.cc",
-  ]
-  deps = [
-    ":lib",
-    ":storage_full",
-    "../../gn:default_deps",
-    "../../gn:gtest_and_gmock",
-    "../base",
-    "../base:test_support",
-    "sqlite",
-  ]
-
-  if (enable_perfetto_trace_processor_json_import) {
-    deps += [ "../../gn:jsoncpp" ]
+  sources = []
+  deps = []
+  if (enable_perfetto_trace_processor_sqlite) {
+    sources += [ "trace_database_integrationtest.cc" ]
+    deps += [
+      ":lib",
+      ":storage_full",
+      "../../gn:default_deps",
+      "../../gn:gtest_and_gmock",
+      "../base",
+      "../base:test_support",
+      "sqlite",
+    ]
+    if (enable_perfetto_trace_processor_json_import) {
+      deps += [ "../../gn:jsoncpp" ]
+    }
   }
 }
 
 if (enable_perfetto_trace_processor_json) {
   source_set("storage_minimal_smoke_tests") {
     testonly = true
-    sources = [
-      "storage_minimal_smoke_test.cc",
-    ]
+    sources = [ "storage_minimal_smoke_test.cc" ]
     deps = [
       ":export_json",
       ":storage_minimal",
@@ -483,9 +484,7 @@
 
 perfetto_fuzzer_test("trace_processor_fuzzer") {
   testonly = true
-  sources = [
-    "trace_parsing_fuzzer.cc",
-  ]
+  sources = [ "trace_parsing_fuzzer.cc" ]
   deps = [
     ":storage_full",
     "../../gn:default_deps",
diff --git a/src/trace_processor/args_table.cc b/src/trace_processor/args_table.cc
deleted file mode 100644
index 64b407c..0000000
--- a/src/trace_processor/args_table.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2018 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/trace_processor/args_table.h"
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-PERFETTO_ALWAYS_INLINE
-bool TreatedAsInteger(Variadic v) {
-  return v.type == Variadic::Type::kInt || v.type == Variadic::Type::kBool ||
-         v.type == Variadic::Type::kPointer || v.type == Variadic::Type::kUint;
-}
-
-PERFETTO_ALWAYS_INLINE
-bool TreatedAsString(Variadic v) {
-  return v.type == Variadic::Type::kString || v.type == Variadic::Type::kJson;
-}
-
-PERFETTO_ALWAYS_INLINE
-int64_t AsInt64(Variadic v) {
-  if (v.type == Variadic::Type::kInt)
-    return v.int_value;
-  if (v.type == Variadic::Type::kBool)
-    return static_cast<int64_t>(v.bool_value);
-  if (v.type == Variadic::Type::kUint)
-    return static_cast<int64_t>(v.uint_value);
-  if (v.type == Variadic::Type::kPointer)
-    return static_cast<int64_t>(v.pointer_value);
-  PERFETTO_FATAL("invalid Variadic type");
-}
-
-PERFETTO_ALWAYS_INLINE
-StringId AsStringId(Variadic v) {
-  if (v.type == Variadic::Type::kString)
-    return v.string_value;
-  if (v.type == Variadic::Type::kJson)
-    return v.json_value;
-  PERFETTO_FATAL("invalid Variadic type");
-}
-}  // namespace
-
-ArgsTable::ArgsTable(sqlite3*, const TraceStorage* storage)
-    : storage_(storage) {}
-
-void ArgsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<ArgsTable>(db, storage, "args");
-}
-
-StorageSchema ArgsTable::CreateStorageSchema() {
-  const auto& args = storage_->args();
-  return StorageSchema::Builder()
-      .AddOrderedNumericColumn("arg_set_id", &args.set_ids())
-      .AddStringColumn("flat_key", &args.flat_keys(), &storage_->string_pool())
-      .AddStringColumn("key", &args.keys(), &storage_->string_pool())
-      .AddColumn<ValueColumn>("int_value", Variadic::Type::kInt, storage_)
-      .AddColumn<ValueColumn>("string_value", Variadic::Type::kString, storage_)
-      .AddColumn<ValueColumn>("real_value", Variadic::Type::kReal, storage_)
-      .Build({"arg_set_id", "key"});
-}
-
-uint32_t ArgsTable::RowCount() {
-  return static_cast<uint32_t>(storage_->args().args_count());
-}
-
-int ArgsTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
-  if (HasEqConstraint(qc, "arg_set_id")) {
-    info->estimated_cost = 1;
-  } else {
-    info->estimated_cost = static_cast<uint32_t>(storage_->args().args_count());
-  }
-  return SQLITE_OK;
-}
-
-ArgsTable::ValueColumn::ValueColumn(std::string col_name,
-                                    Variadic::Type type,
-                                    const TraceStorage* storage)
-    : StorageColumn(col_name, false /* hidden */),
-      type_(type),
-      storage_(storage) {
-  PERFETTO_CHECK(type == Variadic::Type::kInt ||
-                 type == Variadic::Type::kReal ||
-                 type == Variadic::Type::kString);
-}
-
-void ArgsTable::ValueColumn::ReportResult(sqlite3_context* ctx,
-                                          uint32_t row) const {
-  const auto& value = storage_->args().arg_values()[row];
-  switch (type_) {
-    // Integer column, returns all integer-like variadic values (as an int64_t).
-    case Variadic::Type::kInt: {
-      if (!TreatedAsInteger(value)) {
-        sqlite3_result_null(ctx);
-        return;
-      }
-      sqlite_utils::ReportSqliteResult(ctx, AsInt64(value));
-      return;
-    }
-
-      // Float column, returns only float values.
-    case Variadic::Type::kReal: {
-      if (value.type != Variadic::Type::kReal) {
-        sqlite3_result_null(ctx);
-        return;
-      }
-      sqlite_utils::ReportSqliteResult(ctx, value.real_value);
-      return;
-    }
-
-      // String column, returns string & json variadic values (as a string).
-    case Variadic::Type::kString: {
-      if (!TreatedAsString(value)) {
-        sqlite3_result_null(ctx);
-        return;
-      }
-      const char* str = storage_->GetString(AsStringId(value)).c_str();
-      sqlite3_result_text(ctx, str, -1, sqlite_utils::kSqliteStatic);
-      return;
-    }
-
-    case Variadic::Type::kBool:
-    case Variadic::Type::kUint:
-    case Variadic::Type::kPointer:
-    case Variadic::Type::kJson:
-      PERFETTO_FATAL("Unexpected column type");
-  }
-}
-
-ArgsTable::ValueColumn::Bounds ArgsTable::ValueColumn::BoundFilter(
-    int,
-    sqlite3_value*) const {
-  return Bounds{};
-}
-
-void ArgsTable::ValueColumn::Filter(int op,
-                                    sqlite3_value* value,
-                                    FilteredRowIndex* index) const {
-  switch (type_) {
-    // Integer column, returns all integer-like variadic values (as an int64_t).
-    case Variadic::Type::kInt: {
-      bool op_is_null = sqlite_utils::IsOpIsNull(op);
-      auto predicate = sqlite_utils::CreateNumericPredicate<int64_t>(op, value);
-      index->FilterRows(
-          [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
-            const Variadic& arg = storage_->args().arg_values()[row];
-            if (!TreatedAsInteger(arg)) {
-              return op_is_null;
-            }
-            return predicate(AsInt64(arg));
-          });
-      break;
-    }
-
-    // Float column, returns only float values.
-    case Variadic::Type::kReal: {
-      bool op_is_null = sqlite_utils::IsOpIsNull(op);
-      auto predicate = sqlite_utils::CreateNumericPredicate<double>(op, value);
-      index->FilterRows(
-          [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
-            const auto& arg = storage_->args().arg_values()[row];
-            return arg.type == Variadic::Type::kReal ? predicate(arg.real_value)
-                                                     : op_is_null;
-          });
-      break;
-    }
-
-    // String column, returns string & json variadic values (as a string).
-    case Variadic::Type::kString: {
-      auto predicate = sqlite_utils::CreateStringPredicate(op, value);
-      index->FilterRows(
-          [this, &predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
-            const auto& arg = storage_->args().arg_values()[row];
-            if (!TreatedAsString(arg)) {
-              return predicate(nullptr);
-            }
-            return predicate(storage_->GetString(AsStringId(arg)).c_str());
-          });
-      break;
-    }
-    case Variadic::Type::kBool:
-    case Variadic::Type::kUint:
-    case Variadic::Type::kPointer:
-    case Variadic::Type::kJson:
-      PERFETTO_FATAL("Unexpected column type");
-  }
-}
-
-ArgsTable::ValueColumn::Comparator ArgsTable::ValueColumn::Sort(
-    const QueryConstraints::OrderBy& ob) const {
-  if (ob.desc) {
-    return [this](uint32_t f, uint32_t s) { return -CompareRefsAsc(f, s); };
-  }
-  return [this](uint32_t f, uint32_t s) { return CompareRefsAsc(f, s); };
-}
-
-int ArgsTable::ValueColumn::CompareRefsAsc(uint32_t f, uint32_t s) const {
-  const auto& arg_f = storage_->args().arg_values()[f];
-  const auto& arg_s = storage_->args().arg_values()[s];
-  switch (type_) {
-    // Integer column, returns all integer-like variadic values (as an int64_t).
-    case Variadic::Type::kInt: {
-      if (TreatedAsInteger(arg_f) && TreatedAsInteger(arg_s)) {
-        return sqlite_utils::CompareValuesAsc(AsInt64(arg_f), AsInt64(arg_s));
-      } else if (TreatedAsInteger(arg_f)) {
-        return 1;  // second value treated as null
-      } else if (TreatedAsInteger(arg_s)) {
-        return -1;  // first value treated as null
-      }
-      return 0;
-    }
-
-    // Float column, returns only float values.
-    case Variadic::Type::kReal: {
-      if (arg_f.type == Variadic::Type::kReal &&
-          arg_s.type == Variadic::Type::kReal) {
-        return sqlite_utils::CompareValuesAsc(arg_f.real_value,
-                                              arg_s.real_value);
-      } else if (arg_f.type == Variadic::Type::kReal) {
-        return 1;  // second value treated as null
-      } else if (arg_s.type == Variadic::Type::kReal) {
-        return -1;  // first value treated as null
-      }
-      return 0;
-    }
-
-    // String column, returns string & json variadic values (as a string).
-    case Variadic::Type::kString: {
-      if (TreatedAsString(arg_f) && TreatedAsString(arg_s)) {
-        const auto& f_str = storage_->GetString(AsStringId(arg_f));
-        const auto& s_str = storage_->GetString(AsStringId(arg_s));
-        return sqlite_utils::CompareValuesAsc(f_str, s_str);
-      } else if (TreatedAsString(arg_f)) {
-        return 1;  // second value treated as null
-      } else if (TreatedAsString(arg_s)) {
-        return -1;  // first value treated as null
-      }
-      return 0;
-    }
-    case Variadic::Type::kBool:
-    case Variadic::Type::kUint:
-    case Variadic::Type::kPointer:
-    case Variadic::Type::kJson:
-      PERFETTO_FATAL("Unexpected column type");
-  }
-  PERFETTO_FATAL("Never reached");  // for gcc
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/args_table.h b/src/trace_processor/args_table.h
deleted file mode 100644
index 8243457..0000000
--- a/src/trace_processor/args_table.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2018 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_TRACE_PROCESSOR_ARGS_TABLE_H_
-#define SRC_TRACE_PROCESSOR_ARGS_TABLE_H_
-
-#include "src/trace_processor/storage_table.h"
-#include "src/trace_processor/trace_storage.h"
-#include "src/trace_processor/variadic.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class ArgsTable : public StorageTable {
- public:
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  ArgsTable(sqlite3*, const TraceStorage*);
-
-  // StorageTable implementation.
-  StorageSchema CreateStorageSchema() override;
-  uint32_t RowCount() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
- private:
-  class ValueColumn final : public StorageColumn {
-   public:
-    ValueColumn(std::string col_name,
-                Variadic::Type type,
-                const TraceStorage* storage);
-
-    void ReportResult(sqlite3_context* ctx, uint32_t row) const override;
-
-    Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override;
-
-    void Filter(int op, sqlite3_value* value, FilteredRowIndex*) const override;
-
-    Comparator Sort(const QueryConstraints::OrderBy& ob) const override;
-
-    bool HasOrdering() const override { return false; }
-
-    SqlValue::Type GetType() const override {
-      switch (type_) {
-        case Variadic::Type::kInt:
-        case Variadic::Type::kUint:
-        case Variadic::Type::kPointer:
-        case Variadic::Type::kBool:
-          return SqlValue::Type::kLong;
-        case Variadic::Type::kJson:
-        case Variadic::Type::kString:
-          return SqlValue::Type::kString;
-        case Variadic::Type::kReal:
-          return SqlValue::Type::kDouble;
-      }
-      PERFETTO_FATAL("Not reached");  // For gcc
-    }
-
-   private:
-    int CompareRefsAsc(uint32_t f, uint32_t s) const;
-
-    Variadic::Type type_;
-    const TraceStorage* storage_ = nullptr;
-  };
-
-  const TraceStorage* const storage_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_ARGS_TABLE_H_
diff --git a/src/trace_processor/args_table_unittest.cc b/src/trace_processor/args_table_unittest.cc
deleted file mode 100644
index de6bec7..0000000
--- a/src/trace_processor/args_table_unittest.cc
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2018 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/trace_processor/args_table.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-class ArgsTableUnittest : public ::testing::Test {
- public:
-  ArgsTableUnittest() {
-    sqlite3* db = nullptr;
-    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
-    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
-    db_.reset(db);
-
-    context_.storage.reset(new TraceStorage());
-    ArgsTable::RegisterTable(db_.get(), context_.storage.get());
-  }
-
-  void PrepareValidStatement(const std::string& sql) {
-    int size = static_cast<int>(sql.size());
-    sqlite3_stmt* stmt;
-    ASSERT_EQ(sqlite3_prepare_v2(*db_, sql.c_str(), size, &stmt, nullptr),
-              SQLITE_OK);
-    stmt_.reset(stmt);
-  }
-
-  const char* GetColumnAsText(int colId) {
-    return reinterpret_cast<const char*>(sqlite3_column_text(*stmt_, colId));
-  }
-
-  void AssertArgRowValues(int arg_set_id,
-                          const char* flat_key,
-                          const char* key,
-                          base::Optional<int64_t> int_value,
-                          base::Optional<const char*> string_value,
-                          base::Optional<double> real_value);
-
- protected:
-  TraceProcessorContext context_;
-  ScopedDb db_;
-  ScopedStmt stmt_;
-};
-
-// Test helper.
-void ArgsTableUnittest::AssertArgRowValues(
-    int arg_set_id,
-    const char* flat_key,
-    const char* key,
-    base::Optional<int64_t> int_value,
-    base::Optional<const char*> string_value,
-    base::Optional<double> real_value) {
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), arg_set_id);
-  ASSERT_STREQ(GetColumnAsText(1), flat_key);
-  ASSERT_STREQ(GetColumnAsText(2), key);
-  if (int_value.has_value()) {
-    ASSERT_EQ(sqlite3_column_int64(*stmt_, 3), int_value.value());
-  } else {
-    ASSERT_EQ(sqlite3_column_type(*stmt_, 3), SQLITE_NULL);
-  }
-  if (string_value.has_value()) {
-    ASSERT_STREQ(GetColumnAsText(4), string_value.value());
-  } else {
-    ASSERT_EQ(sqlite3_column_type(*stmt_, 4), SQLITE_NULL);
-  }
-  if (real_value.has_value()) {
-    ASSERT_DOUBLE_EQ(sqlite3_column_double(*stmt_, 5), real_value.value());
-  } else {
-    ASSERT_EQ(sqlite3_column_type(*stmt_, 5), SQLITE_NULL);
-  }
-}
-
-TEST_F(ArgsTableUnittest, IntValue) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const int kValue = 123;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::Integer(kValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  PrepareValidStatement("SELECT * FROM args");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, kValue, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, StringValue) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const char kValue[] = "123";
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::String(context_.storage->InternString(kValue));
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  PrepareValidStatement("SELECT * FROM args");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, kValue, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, RealValue) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const double kValue = 0.123;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::Real(kValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  PrepareValidStatement("SELECT * FROM args");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, base::nullopt, kValue);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, BoolValueTreatedAsInt) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const bool kValue = true;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::Boolean(kValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  // Boolean returned in the "int_value" column, and is comparable to an integer
-  // literal.
-  PrepareValidStatement("SELECT * FROM args WHERE int_value = 1");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, kValue, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, PointerValueTreatedAsInt) {
-  static const uint64_t kSmallValue = 1ull << 30;
-  static const uint64_t kTopBitSetValue = 1ull << 63;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString("flat_key_small");
-  arg.key = context_.storage->InternString("key_small");
-  arg.value = Variadic::Pointer(kSmallValue);
-
-  TraceStorage::Args::Arg arg2;
-  arg2.flat_key = context_.storage->InternString("flat_key_large");
-  arg2.key = context_.storage->InternString("key_large");
-  arg2.value = Variadic::Pointer(kTopBitSetValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg, arg2}, 0, 2);
-
-  // Pointer returned in the "int_value" column, as a signed 64 bit. And is
-  // comparable to an integer literal.
-
-  static const int64_t kExpectedSmallValue = static_cast<int64_t>(kSmallValue);
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedSmallValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_small", "key_small", kExpectedSmallValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-
-  static const int64_t kExpectedTopBitSetValue =
-      static_cast<int64_t>(kTopBitSetValue);  // negative
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedTopBitSetValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_large", "key_large", kExpectedTopBitSetValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, UintValueTreatedAsInt) {
-  static const uint64_t kSmallValue = 1ull << 30;
-  static const uint64_t kTopBitSetValue = 1ull << 63;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString("flat_key_small");
-  arg.key = context_.storage->InternString("key_small");
-  arg.value = Variadic::UnsignedInteger(kSmallValue);
-
-  TraceStorage::Args::Arg arg2;
-  arg2.flat_key = context_.storage->InternString("flat_key_large");
-  arg2.key = context_.storage->InternString("key_large");
-  arg2.value = Variadic::UnsignedInteger(kTopBitSetValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg, arg2}, 0, 2);
-
-  // Unsigned returned in the "int_value" column, as a signed 64 bit. And is
-  // comparable to an integer literal.
-
-  static const int64_t kExpectedSmallValue = static_cast<int64_t>(kSmallValue);
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedSmallValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_small", "key_small", kExpectedSmallValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-
-  static const int64_t kExpectedTopBitSetValue =
-      static_cast<int64_t>(kTopBitSetValue);  // negative
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedTopBitSetValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_large", "key_large", kExpectedTopBitSetValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, IntegerLikeValuesSortByIntRepresentation) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-
-  TraceStorage::Args::Arg bool_arg_true;
-  bool_arg_true.flat_key = context_.storage->InternString(kFlatKey);
-  bool_arg_true.key = context_.storage->InternString(kKey);
-  bool_arg_true.value = Variadic::Boolean(true);
-
-  TraceStorage::Args::Arg bool_arg_false;
-  bool_arg_false.flat_key = context_.storage->InternString(kFlatKey);
-  bool_arg_false.key = context_.storage->InternString(kKey);
-  bool_arg_false.value = Variadic::Boolean(false);
-
-  TraceStorage::Args::Arg pointer_arg_42;
-  pointer_arg_42.flat_key = context_.storage->InternString(kFlatKey);
-  pointer_arg_42.key = context_.storage->InternString(kKey);
-  pointer_arg_42.value = Variadic::Pointer(42);
-
-  TraceStorage::Args::Arg unsigned_arg_10;
-  unsigned_arg_10.flat_key = context_.storage->InternString(kFlatKey);
-  unsigned_arg_10.key = context_.storage->InternString(kKey);
-  unsigned_arg_10.value = Variadic::UnsignedInteger(10);
-
-  // treated as null by the int_value column
-  TraceStorage::Args::Arg string_arg;
-  string_arg.flat_key = context_.storage->InternString(kFlatKey);
-  string_arg.key = context_.storage->InternString(kKey);
-  string_arg.value =
-      Variadic::String(context_.storage->InternString("string_content"));
-
-  context_.storage->mutable_args()->AddArgSet(
-      {bool_arg_true, bool_arg_false, pointer_arg_42, unsigned_arg_10,
-       string_arg},
-      0, 5);
-
-  // Ascending sort by int representations:
-  // { null (string), 0 (false), 1 (true), 10, 42 }
-  PrepareValidStatement("SELECT * FROM args ORDER BY int_value ASC");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, "string_content",
-                     base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 0, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 1, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 10, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 42, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-
-  // Desceding order.
-  PrepareValidStatement("SELECT * FROM args ORDER BY int_value DESC");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 42, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 10, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 1, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 0, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, "string_content",
-                     base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/args_tracker.cc b/src/trace_processor/args_tracker.cc
index 4f143c5..a0114ce 100644
--- a/src/trace_processor/args_tracker.cc
+++ b/src/trace_processor/args_tracker.cc
@@ -27,7 +27,7 @@
   Flush();
 }
 
-void ArgsTracker::AddArg(TableId table,
+void ArgsTracker::AddArg(Column* arg_set_id,
                          uint32_t row,
                          StringId flat_key,
                          StringId key,
@@ -35,7 +35,7 @@
   args_.emplace_back();
 
   auto* rid_arg = &args_.back();
-  rid_arg->table = table;
+  rid_arg->column = arg_set_id;
   rid_arg->row = row;
   rid_arg->flat_key = flat_key;
   rid_arg->key = key;
@@ -43,7 +43,7 @@
 }
 
 void ArgsTracker::Flush() {
-  using Arg = TraceStorage::Args::Arg;
+  using Arg = GlobalArgsTracker::Arg;
 
   if (args_.empty())
     return;
@@ -51,63 +51,42 @@
   // We sort here because a single packet may add multiple args with different
   // rowids.
   auto comparator = [](const Arg& f, const Arg& s) {
-    return f.table < s.table && f.row < s.row;
+    // We only care that all args for a specific arg set appear in a contiguous
+    // block, but not about the relative order of one block to another. The
+    // simplest way to achieve that is to sort by table column pointer & row,
+    // which identify the arg set.
+    return f.column < s.column && f.row < s.row;
   };
   std::stable_sort(args_.begin(), args_.end(), comparator);
 
-  auto* storage = context_->storage.get();
   for (uint32_t i = 0; i < args_.size();) {
     const auto& arg = args_[i];
-    auto table_id = arg.table;
+    Column* column = arg.column;
     auto row = arg.row;
 
     uint32_t next_rid_idx = i + 1;
     while (next_rid_idx < args_.size() &&
-           table_id == args_[next_rid_idx].table &&
+           column == args_[next_rid_idx].column &&
            row == args_[next_rid_idx].row) {
       next_rid_idx++;
     }
 
     ArgSetId set_id =
-        storage->mutable_args()->AddArgSet(args_, i, next_rid_idx);
-    switch (table_id) {
-      case TableId::kRawEvents:
-        storage->mutable_raw_events()->set_arg_set_id(row, set_id);
-        break;
-      case TableId::kCounterValues:
-        storage->mutable_counter_table()->mutable_arg_set_id()->Set(row,
-                                                                    set_id);
-        break;
-      case TableId::kInstants:
-        storage->mutable_instant_table()->mutable_arg_set_id()->Set(row,
-                                                                    set_id);
-        break;
-      case TableId::kNestableSlices:
-        storage->mutable_slice_table()->mutable_arg_set_id()->Set(row, set_id);
-        break;
-      // Special case: overwrites the metadata table row.
-      case TableId::kMetadataTable:
-        storage->mutable_metadata_table()->mutable_int_value()->Set(row,
-                                                                    set_id);
-        break;
-      case TableId::kTrack:
-        storage->mutable_track_table()->mutable_source_arg_set_id()->Set(
-            row, set_id);
-        break;
-      case TableId::kVulkanMemoryAllocation:
-        storage->mutable_vulkan_memory_allocations_table()
-            ->mutable_arg_set_id()
-            ->Set(row, set_id);
-        break;
-      case TableId::kInvalid:
-      case TableId::kSched:
-        PERFETTO_FATAL("Unsupported table to insert args into");
-    }
+        context_->global_args_tracker->AddArgSet(args_, i, next_rid_idx);
+    column->Set(row, SqlValue::Long(set_id));
+
     i = next_rid_idx;
   }
   args_.clear();
 }
 
+ArgsTracker::BoundInserter::BoundInserter(ArgsTracker* args_tracker,
+                                          Column* arg_set_id_column,
+                                          uint32_t row)
+    : args_tracker_(args_tracker),
+      arg_set_id_column_(arg_set_id_column),
+      row_(row) {}
+
 ArgsTracker::BoundInserter::~BoundInserter() {}
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/args_tracker.h b/src/trace_processor/args_tracker.h
index 90062a8..9acccfb 100644
--- a/src/trace_processor/args_tracker.h
+++ b/src/trace_processor/args_tracker.h
@@ -17,9 +17,10 @@
 #ifndef SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
 
+#include "src/trace_processor/global_args_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/trace_storage.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -33,41 +34,86 @@
   // args should be associated with.
   class BoundInserter {
    public:
-    BoundInserter(ArgsTracker* args_tracker, TableId table, uint32_t row)
-        : args_tracker_(args_tracker), table_(table), row_(row) {}
     virtual ~BoundInserter();
 
     // Adds an arg with the same key and flat_key.
-    void AddArg(StringId key, Variadic v) { AddArg(key, key, v); }
-
-    // Virtual for testing.
-    virtual void AddArg(StringId flat_key, StringId key, Variadic v) {
-      args_tracker_->AddArg(table_, row_, flat_key, key, v);
+    BoundInserter& AddArg(StringId key, Variadic v) {
+      return AddArg(key, key, v);
     }
 
+    // Virtual for testing.
+    virtual BoundInserter& AddArg(StringId flat_key, StringId key, Variadic v) {
+      args_tracker_->AddArg(arg_set_id_column_, row_, flat_key, key, v);
+      return *this;
+    }
+
+   protected:
+    BoundInserter(ArgsTracker* args_tracker,
+                  Column* arg_set_id_column,
+                  uint32_t row);
+
    private:
+    friend class ArgsTracker;
+
     ArgsTracker* args_tracker_ = nullptr;
-    TableId table_ = TableId::kInvalid;
+    Column* arg_set_id_column_ = nullptr;
     uint32_t row_ = 0;
   };
 
   explicit ArgsTracker(TraceProcessorContext*);
   virtual ~ArgsTracker();
 
-  // Adds a arg for this row id with the given key and value.
-  // Virtual for testing.
-  virtual void AddArg(TableId table,
-                      uint32_t row,
-                      StringId flat_key,
-                      StringId key,
-                      Variadic);
+  BoundInserter AddArgsTo(RawId id) {
+    return AddArgsTo(context_->storage->mutable_raw_table(), id);
+  }
+
+  BoundInserter AddArgsTo(CounterId id) {
+    return AddArgsTo(context_->storage->mutable_counter_table(), id);
+  }
+
+  BoundInserter AddArgsTo(InstantId id) {
+    return AddArgsTo(context_->storage->mutable_instant_table(), id);
+  }
+
+  BoundInserter AddArgsTo(SliceId id) {
+    return AddArgsTo(context_->storage->mutable_slice_table(), id);
+  }
+
+  BoundInserter AddArgsTo(MetadataId id) {
+    auto* table = context_->storage->mutable_metadata_table();
+    uint32_t row = *table->id().IndexOf(id);
+    return BoundInserter(this, table->mutable_int_value(), row);
+  }
+
+  BoundInserter AddArgsTo(TrackId id) {
+    auto* table = context_->storage->mutable_track_table();
+    uint32_t row = *table->id().IndexOf(id);
+    return BoundInserter(this, table->mutable_source_arg_set_id(), row);
+  }
+
+  BoundInserter AddArgsTo(VulkanAllocId id) {
+    return AddArgsTo(
+        context_->storage->mutable_vulkan_memory_allocations_table(), id);
+  }
 
   // Commits the added args to storage.
   // Virtual for testing.
   virtual void Flush();
 
  private:
-  std::vector<TraceStorage::Args::Arg> args_;
+  template <typename Table>
+  BoundInserter AddArgsTo(Table* table, typename Table::Id id) {
+    uint32_t row = *table->id().IndexOf(id);
+    return BoundInserter(this, table->mutable_arg_set_id(), row);
+  }
+
+  void AddArg(Column* arg_set_id,
+              uint32_t row,
+              StringId flat_key,
+              StringId key,
+              Variadic);
+
+  std::vector<GlobalArgsTracker::Arg> args_;
   TraceProcessorContext* const context_;
 };
 
diff --git a/src/trace_processor/clock_tracker.cc b/src/trace_processor/clock_tracker.cc
index 696a110..43a6819 100644
--- a/src/trace_processor/clock_tracker.cc
+++ b/src/trace_processor/clock_tracker.cc
@@ -41,6 +41,10 @@
 void ClockTracker::AddSnapshot(const std::vector<ClockValue>& clocks) {
   const auto snapshot_id = cur_snapshot_id_++;
 
+  // Clear the cache
+  static_assert(std::is_trivial<decltype(cache_)>::value, "must be trivial");
+  memset(&cache_[0], 0, sizeof(cache_));
+
   // Compute the fingerprint of the snapshot by hashing all clock ids. This is
   // used by the clock pathfinding logic.
   base::Hash hasher;
@@ -192,25 +196,33 @@
   return ClockPath();  // invalid path.
 }
 
-base::Optional<int64_t> ClockTracker::Convert(ClockId src_clock_id,
-                                              int64_t src_timestamp,
-                                              ClockId target_clock_id) {
-  // TODO(primiano): optimization: I bet A simple LRU cache of the form
-  // (src_clock_id, target_clock_id, latest_timestamp, translation_ns) might
-  // speed up most conversion allowing to skip FindPath and the iterations.
-
+base::Optional<int64_t> ClockTracker::ConvertSlowpath(ClockId src_clock_id,
+                                                      int64_t src_timestamp,
+                                                      ClockId target_clock_id) {
   PERFETTO_DCHECK(!IsReservedSeqScopedClockId(src_clock_id));
   PERFETTO_DCHECK(!IsReservedSeqScopedClockId(target_clock_id));
 
+  context_->storage->IncrementStats(stats::clock_sync_cache_miss);
+
   ClockPath path = FindPath(src_clock_id, target_clock_id);
   if (!path.valid()) {
     context_->storage->IncrementStats(stats::clock_sync_failure);
     return base::nullopt;
   }
 
+  // We can cache only single-path resolutions between two clocks.
+  // Caching multi-path resolutions is harder because the (src,target) tuple
+  // is not enough as a cache key: at any step the |ns| value can yield to a
+  // different choice of the next snapshot. Multi-path resolutions don't seem
+  // too frequent these days, so we focus only on caching the more frequent
+  // one-step resolutions (typically from any clock to the trace clock).
+  const bool cacheable = path.len == 1;
+  CachedClockPath cache_entry{};
+
   // Iterate trough the path found and translate timestamps onto the new clock
   // domain on each step, until the target domain is reached.
-  int64_t ns = GetClock(src_clock_id)->ToNs(src_timestamp);
+  ClockDomain* src_domain = GetClock(src_clock_id);
+  int64_t ns = src_domain->ToNs(src_timestamp);
   for (uint32_t i = 0; i < path.len; ++i) {
     const ClockGraphEdge edge = path.at(i);
     ClockDomain* cur_clock = GetClock(std::get<0>(edge));
@@ -246,12 +258,33 @@
     // The translated timestamp is the relative delta of the source timestamp
     // from the closest snapshot found (ns - *it), plus the timestamp in
     // the new clock domain for the same snapshot id.
-    ns = (ns - *it) + next_timestamp_ns;
+    const int64_t adj = next_timestamp_ns - *it;
+    ns += adj;
+
+    // On the first iteration, keep track of the bounds for the cache entry.
+    // This will allow future Convert() calls to skip the pathfinder logic
+    // as long as the query stays within the bound.
+    if (cacheable) {
+      PERFETTO_DCHECK(i == 0);
+      const int64_t kInt64Min = std::numeric_limits<int64_t>::min();
+      const int64_t kInt64Max = std::numeric_limits<int64_t>::max();
+      cache_entry.min_ts_ns = it == ts_vec.begin() ? kInt64Min : *it;
+      auto ubound = it + 1;
+      cache_entry.max_ts_ns = ubound == ts_vec.end() ? kInt64Max : *ubound;
+      cache_entry.translation_ns = adj;
+    }
 
     // The last clock in the path must be the target clock.
     PERFETTO_DCHECK(i < path.len - 1 || std::get<1>(edge) == target_clock_id);
   }
 
+  if (cacheable) {
+    cache_entry.src = src_clock_id;
+    cache_entry.src_domain = src_domain;
+    cache_entry.target = target_clock_id;
+    cache_[rnd_() % cache_.size()] = cache_entry;
+  }
+
   return ns;
 }
 
diff --git a/src/trace_processor/clock_tracker.h b/src/trace_processor/clock_tracker.h
index c261249..63fe8e1 100644
--- a/src/trace_processor/clock_tracker.h
+++ b/src/trace_processor/clock_tracker.h
@@ -21,6 +21,7 @@
 
 #include <array>
 #include <map>
+#include <random>
 #include <set>
 #include <vector>
 
@@ -154,9 +155,27 @@
   // This is typically called by the code that reads the ClockSnapshot packet.
   void AddSnapshot(const std::vector<ClockValue>&);
 
+  // Converts a timestamp between two clock domains. Tries to use the cache
+  // first (only for single-path resolutions), then falls back on path finding
+  // as described in the header.
   base::Optional<int64_t> Convert(ClockId src_clock_id,
                                   int64_t src_timestamp,
-                                  ClockId target_clock_id);
+                                  ClockId target_clock_id) {
+    if (PERFETTO_LIKELY(!cache_lookups_disabled_for_testing_)) {
+      for (const auto& ce : cache_) {
+        if (ce.src != src_clock_id || ce.target != target_clock_id)
+          continue;
+        int64_t ns = ce.src_domain->ToNs(src_timestamp);
+        if (ns >= ce.min_ts_ns && ns < ce.max_ts_ns)
+          return ns + ce.translation_ns;
+      }
+    }
+    return ConvertSlowpath(src_clock_id, src_timestamp, target_clock_id);
+  }
+
+  base::Optional<int64_t> ConvertSlowpath(ClockId src_clock_id,
+                                          int64_t src_timestamp,
+                                          ClockId target_clock_id);
 
   base::Optional<int64_t> ToTraceTime(ClockId clock_id, int64_t timestamp) {
     if (clock_id == trace_time_clock_id_)
@@ -169,6 +188,10 @@
     trace_time_clock_id_ = clock_id;
   }
 
+  void set_cache_lookups_disabled_for_testing(bool v) {
+    cache_lookups_disabled_for_testing_ = v;
+  }
+
  private:
   using SnapshotHash = uint32_t;
 
@@ -245,6 +268,17 @@
     }
   };
 
+  // Holds data for cached entries. At the moment only single-path resolution
+  // are cached.
+  struct CachedClockPath {
+    ClockId src;
+    ClockId target;
+    ClockDomain* src_domain;
+    int64_t min_ts_ns;
+    int64_t max_ts_ns;
+    int64_t translation_ns;
+  };
+
   ClockTracker(const ClockTracker&) = delete;
   ClockTracker& operator=(const ClockTracker&) = delete;
 
@@ -261,6 +295,9 @@
   std::map<ClockId, ClockDomain> clocks_;
   std::set<ClockGraphEdge> graph_;
   std::set<ClockId> non_monotonic_clocks_;
+  std::array<CachedClockPath, 2> cache_{};
+  bool cache_lookups_disabled_for_testing_ = false;
+  std::minstd_rand rnd_;  // For cache eviction.
   uint32_t cur_snapshot_id_ = 0;
 };
 
diff --git a/src/trace_processor/clock_tracker_unittest.cc b/src/trace_processor/clock_tracker_unittest.cc
index e1628af..2fc8e59 100644
--- a/src/trace_processor/clock_tracker_unittest.cc
+++ b/src/trace_processor/clock_tracker_unittest.cc
@@ -16,6 +16,8 @@
 
 #include "src/trace_processor/clock_tracker.h"
 
+#include <random>
+
 #include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/trace_storage.h"
@@ -207,6 +209,55 @@
   EXPECT_EQ(*ct_.ToTraceTime(c66_2, 4 /* abs 30 */), 129000);
 }
 
+// Tests that the cache doesn't affect the results of Convert() in unexpected
+// ways.
+TEST_F(ClockTrackerTest, CacheDoesntAffectResults) {
+  std::minstd_rand rnd;
+  int last_mono = 0;
+  int last_boot = 0;
+  int last_raw = 0;
+  static const int increments[] = {1, 2, 10};
+  for (int i = 0; i < 1000; i++) {
+    last_mono += increments[rnd() % base::ArraySize(increments)];
+    last_boot += increments[rnd() % base::ArraySize(increments)];
+    ct_.AddSnapshot({{MONOTONIC, last_mono}, {BOOTTIME, last_boot}});
+
+    last_raw += increments[rnd() % base::ArraySize(increments)];
+    last_boot += increments[rnd() % base::ArraySize(increments)];
+    ct_.AddSnapshot({{MONOTONIC_RAW, last_raw}, {BOOTTIME, last_boot}});
+  }
+
+  for (int i = 0; i < 1000; i++) {
+    int64_t val = static_cast<int64_t>(rnd()) % 10000;
+    for (int j = 0; j < 5; j++) {
+      ClockTracker::ClockId src;
+      ClockTracker::ClockId tgt;
+      if (j == 0) {
+        std::tie(src, tgt) = std::make_tuple(MONOTONIC, BOOTTIME);
+      } else if (j == 1) {
+        std::tie(src, tgt) = std::make_tuple(MONOTONIC_RAW, BOOTTIME);
+      } else if (j == 2) {
+        std::tie(src, tgt) = std::make_tuple(BOOTTIME, MONOTONIC);
+      } else if (j == 3) {
+        std::tie(src, tgt) = std::make_tuple(BOOTTIME, MONOTONIC_RAW);
+      } else if (j == 4) {
+        std::tie(src, tgt) = std::make_tuple(MONOTONIC_RAW, MONOTONIC);
+      } else {
+        PERFETTO_FATAL("j out of bounds");
+      }
+      // It will still write the cache, just not lookup.
+      ct_.set_cache_lookups_disabled_for_testing(true);
+      auto not_cached = ct_.Convert(src, val, tgt);
+
+      // This should 100% hit the cache.
+      ct_.set_cache_lookups_disabled_for_testing(false);
+      auto cached = ct_.Convert(src, val, tgt);
+
+      ASSERT_EQ(not_cached, cached);
+    }
+  }
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/containers/bit_vector.cc b/src/trace_processor/containers/bit_vector.cc
index 0ec5bcc..a2308f3 100644
--- a/src/trace_processor/containers/bit_vector.cc
+++ b/src/trace_processor/containers/bit_vector.cc
@@ -54,19 +54,19 @@
   return SetBitsIterator(this);
 }
 
-void BitVector::UpdateSetBits(const BitVector& other) {
-  PERFETTO_DCHECK(other.size() == GetNumBitsSet());
+void BitVector::UpdateSetBits(const BitVector& o) {
+  PERFETTO_DCHECK(o.size() <= GetNumBitsSet());
 
-  // For each set bit in this bitvector, we lookup whether |other| has the
-  // bit set. If not, we clear the bit.
+  // For each set bit in this bitvector, we lookup whether the bit in |other|
+  // at that index (if in bounds) is set. If not, we clear the bit.
   for (auto it = IterateSetBits(); it; it.Next()) {
-    if (!other.IsSet(it.ordinal()))
+    if (it.ordinal() >= o.size() || !o.IsSet(it.ordinal()))
       it.Clear();
   }
 
   // After the loop, we should have precisely the same number of bits
   // set as |other|.
-  PERFETTO_DCHECK(GetNumBitsSet() == other.GetNumBitsSet());
+  PERFETTO_DCHECK(o.GetNumBitsSet() == GetNumBitsSet());
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h
index 58b9e23..bfd8b4f 100644
--- a/src/trace_processor/containers/bit_vector.h
+++ b/src/trace_processor/containers/bit_vector.h
@@ -107,8 +107,8 @@
     PERFETTO_DCHECK(it != counts_.begin());
 
     // Go back one block to find the block which has the bit we are looking for.
-    uint16_t block_idx =
-        static_cast<uint16_t>(std::distance(counts_.begin(), it) - 1);
+    uint32_t block_idx =
+        static_cast<uint32_t>(std::distance(counts_.begin(), it) - 1);
 
     // Figure out how many set bits forward we are looking inside the block
     // by taking away the number of bits at the start of the block from n.
@@ -265,6 +265,42 @@
     size_ = size;
   }
 
+  // Creates a BitVector of size |end| with the bits between |start| and |end|
+  // filled by calling the filler function |f(index of bit)|.
+  //
+  // As an example, suppose Range(3, 7, [](x) { return x < 5 }). This would
+  // result in the following bitvector:
+  // [0 0 0 1 1 0 0 0]
+  template <typename Filler = bool(uint32_t)>
+  static BitVector Range(uint32_t start, uint32_t end, Filler f) {
+    // Compute the block index and bitvector index where we start and end
+    // working one block at a time.
+    uint32_t start_fast_block = BlockCeil(start);
+    uint32_t start_fast_idx = BlockToIndex(start_fast_block);
+    uint32_t end_fast_block = BlockFloor(end);
+    uint32_t end_fast_idx = BlockToIndex(end_fast_block);
+
+    // First, create the BitVector up to |start| then fill up to
+    // |start_fast_index| with values from the filler.
+    BitVector bv(start, false);
+    for (uint32_t i = start; i < start_fast_idx; ++i) {
+      bv.Append(f(i));
+    }
+
+    // At this point we can work one block at a time.
+    for (uint32_t i = start_fast_block; i < end_fast_block; ++i) {
+      bv.counts_.emplace_back(bv.GetNumBitsSet());
+      bv.blocks_.emplace_back(Block::FromFiller(bv.size_, f));
+      bv.size_ += Block::kBits;
+    }
+
+    // Add the last few elements to finish up to |end|.
+    for (uint32_t i = end_fast_idx; i < end; ++i) {
+      bv.Append(f(i));
+    }
+    return bv;
+  }
+
   // Updates the ith set bit of this bitvector with the value of
   // |other.IsSet(i)|.
   //
@@ -296,6 +332,17 @@
   // }
   SetBitsIterator IterateSetBits() const;
 
+  // Returns the approximate cost (in bytes) of storing a bitvector with size
+  // |n|. This can be used to make decisions about whether using a BitVector is
+  // worthwhile.
+  // This cost should not be treated as exact - it just gives an indication of
+  // the memory needed.
+  static constexpr uint32_t ApproxBytesCost(uint32_t n) {
+    // The two main things making up a bitvector is the cost of the blocks of
+    // bits and the cost of the counts vector.
+    return BlockCeil(n) * Block::kBits + BlockCeil(n) * sizeof(uint32_t);
+  }
+
  private:
   friend class internal::BaseIterator;
   friend class internal::AllBitsIterator;
@@ -325,15 +372,18 @@
     // Returns whether the bit at the given index is set.
     bool IsSet(uint32_t idx) const {
       PERFETTO_DCHECK(idx < kBits);
-      return (word >> idx) & 1ull;
+      return (word_ >> idx) & 1ull;
     }
 
+    // Bitwise ors the given |mask| to the current value.
+    void Or(uint64_t mask) { word_ |= mask; }
+
     // Sets the bit at the given index to true.
     void Set(uint32_t idx) {
       PERFETTO_DCHECK(idx < kBits);
 
       // Or the value for the true shifted up to |idx| with the word.
-      word |= 1ull << idx;
+      Or(1ull << idx);
     }
 
     // Sets the bit at the given index to false.
@@ -341,11 +391,11 @@
       PERFETTO_DCHECK(idx < kBits);
 
       // And the integer of all bits set apart from |idx| with the word.
-      word &= ~(1ull << idx);
+      word_ &= ~(1ull << idx);
     }
 
     // Clears all the bits (i.e. sets the atom to zero).
-    void ClearAll() { word = 0; }
+    void ClearAll() { word_ = 0; }
 
     // Returns the index of the nth set bit.
     // Undefined if |n| >= |GetNumBitsSet()|.
@@ -367,13 +417,13 @@
       //
       // The code below was taken from the paper
       // http://vigna.di.unimi.it/ftp/papers/Broadword.pdf
-      uint64_t s = word - ((word & 0xAAAAAAAAAAAAAAAA) >> 1);
+      uint64_t s = word_ - ((word_ & 0xAAAAAAAAAAAAAAAA) >> 1);
       s = (s & 0x3333333333333333) + ((s >> 2) & 0x3333333333333333);
       s = ((s + (s >> 4)) & 0x0F0F0F0F0F0F0F0F) * L8;
 
       uint64_t b = (BwLessThan(s, n * L8) >> 7) * L8 >> 53 & ~7ull;
       uint64_t l = n - ((s << 8) >> b & 0xFF);
-      s = (BwGtZero(((word >> b & 0xFF) * L8) & 0x8040201008040201) >> 7) * L8;
+      s = (BwGtZero(((word_ >> b & 0xFF) * L8) & 0x8040201008040201) >> 7) * L8;
 
       uint64_t ret = b + ((BwLessThan(s, l * L8) >> 7) * L8 >> 56);
 
@@ -384,7 +434,7 @@
     uint32_t GetNumBitsSet() const {
       // We use __builtin_popcountll here as it's available natively for the two
       // targets we care most about (x64 and WASM).
-      return static_cast<uint32_t>(__builtin_popcountll(word));
+      return static_cast<uint32_t>(__builtin_popcountll(word_));
     }
 
     // Returns the number of set bits up to and including the bit at |idx|.
@@ -400,13 +450,13 @@
     // all bits after this point.
     void ClearAfter(uint32_t idx) {
       PERFETTO_DCHECK(idx < kBits);
-      word = WordUntil(idx);
+      word_ = WordUntil(idx);
     }
 
     // Sets all bits between the bit at |start| and |end| (inclusive).
     void Set(uint32_t start, uint32_t end) {
       uint32_t diff = end - start;
-      word |= (MaskAllBitsSetUntil(diff) << static_cast<uint64_t>(start));
+      word_ |= (MaskAllBitsSetUntil(diff) << static_cast<uint64_t>(start));
     }
 
    private:
@@ -447,7 +497,7 @@
       uint64_t mask = MaskAllBitsSetUntil(idx);
 
       // Finish up by anding the the atom with the computed msk.
-      return word & mask;
+      return word_ & mask;
     }
 
     // Return a mask of all the bits up to and including bit at |idx|.
@@ -468,7 +518,7 @@
       return top - 1u;
     }
 
-    uint64_t word = 0;
+    uint64_t word_ = 0;
   };
 
   // Represents a group of bits with a bitcount such that it is
@@ -483,7 +533,7 @@
   class Block {
    public:
     // See class documentation for how these constants are chosen.
-    static constexpr uint32_t kWords = 8;
+    static constexpr uint16_t kWords = 8;
     static constexpr uint32_t kBits = kWords * BitWord::kBits;
 
     // Returns whether the bit at the given address is set.
@@ -589,6 +639,24 @@
       words_[end.word_idx].Set(0, end.bit_idx);
     }
 
+    template <typename Filler>
+    static Block FromFiller(uint32_t offset, Filler f) {
+      // We choose to iterate the bits as the outer loop as this allows us
+      // to reuse the mask and the bit offset between iterations of the loop.
+      // This makes a small (but noticable) impact in the performance of this
+      // function.
+      Block b;
+      for (uint32_t i = 0; i < BitWord::kBits; ++i) {
+        uint64_t mask = 1ull << i;
+        uint32_t offset_with_bit = offset + i;
+        for (uint32_t j = 0; j < Block::kWords; ++j) {
+          bool res = f(offset_with_bit + j * BitWord::kBits);
+          b.words_[j].Or(res ? mask : 0);
+        }
+      }
+      return b;
+    }
+
    private:
     std::array<BitWord, kWords> words_{};
   };
@@ -631,6 +699,17 @@
     blocks_[end.block_idx].Set(kFirstBlockOffset, end.block_offset);
   }
 
+  // Helper function to append a bit. Generally, prefer to call AppendTrue
+  // or AppendFalse instead of this function if you know the type - they will
+  // be faster.
+  void Append(bool value) {
+    if (value) {
+      AppendTrue();
+    } else {
+      AppendFalse();
+    }
+  }
+
   static Address IndexToAddress(uint32_t idx) {
     Address a;
     a.block_idx = idx / Block::kBits;
@@ -647,6 +726,29 @@
            addr.block_offset.bit_idx;
   }
 
+  // Rounds |idx| up to the nearest block boundary and returns the block
+  // index. If |idx| is already on a block boundary, the current block is
+  // returned.
+  //
+  // This is useful to be able to find indices where "fast" algorithms can start
+  // which work on entire blocks.
+  static constexpr uint32_t BlockCeil(uint32_t idx) {
+    // Adding |Block::kBits - 1| gives us a quick way to get the ceil. We
+    // do this instead of adding 1 at the end because that gives incorrect
+    // answers for index % Block::kBits == 0.
+    return (idx + Block::kBits - 1) / Block::kBits;
+  }
+
+  // Returns the index of the block which would store |idx|.
+  static constexpr uint32_t BlockFloor(uint32_t idx) {
+    return idx / Block::kBits;
+  }
+
+  // Converts a block index to a index in the BitVector.
+  static constexpr uint32_t BlockToIndex(uint32_t block) {
+    return block * Block::kBits;
+  }
+
   uint32_t size_ = 0;
   std::vector<uint32_t> counts_;
   std::vector<Block> blocks_;
diff --git a/src/trace_processor/containers/bit_vector_benchmark.cc b/src/trace_processor/containers/bit_vector_benchmark.cc
index b4a0c27..6578e73 100644
--- a/src/trace_processor/containers/bit_vector_benchmark.cc
+++ b/src/trace_processor/containers/bit_vector_benchmark.cc
@@ -17,6 +17,7 @@
 #include <benchmark/benchmark.h>
 
 #include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/containers/bit_vector_iterators.h"
 
 namespace {
 
@@ -27,15 +28,40 @@
 }
 
 void BitVectorArgs(benchmark::internal::Benchmark* b) {
-  b->Arg(64);
+  std::vector<int> set_percentages;
+  if (IsBenchmarkFunctionalOnly()) {
+    set_percentages = std::vector<int>{50};
+  } else {
+    set_percentages = std::vector<int>{0, 1, 5, 50, 95, 99, 100};
+  }
 
-  if (!IsBenchmarkFunctionalOnly()) {
-    b->Arg(512);
-    b->Arg(8192);
-    b->Arg(123456);
-    b->Arg(1234567);
+  for (int percentage : set_percentages) {
+    b->Args({64, percentage});
+
+    if (!IsBenchmarkFunctionalOnly()) {
+      b->Args({512, percentage});
+      b->Args({8192, percentage});
+      b->Args({123456, percentage});
+      b->Args({1234567, percentage});
+    }
   }
 }
+
+BitVector BvWithSizeAndSetPercentage(uint32_t size, uint32_t set_percentage) {
+  static constexpr uint32_t kRandomSeed = 29;
+  std::minstd_rand0 rnd_engine(kRandomSeed);
+
+  BitVector bv;
+  for (uint32_t i = 0; i < size; ++i) {
+    if (rnd_engine() % 100 < set_percentage) {
+      bv.AppendTrue();
+    } else {
+      bv.AppendFalse();
+    }
+  }
+  return bv;
+}
+
 }  // namespace
 
 static void BM_BitVectorAppendTrue(benchmark::State& state) {
@@ -61,15 +87,9 @@
   std::minstd_rand0 rnd_engine(kRandomSeed);
 
   uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
 
-  BitVector bv;
-  for (uint32_t i = 0; i < size; ++i) {
-    if (rnd_engine() % 2) {
-      bv.AppendTrue();
-    } else {
-      bv.AppendFalse();
-    }
-  }
+  BitVector bv = BvWithSizeAndSetPercentage(size, set_percentage);
 
   static constexpr uint32_t kPoolSize = 1024 * 1024;
   std::vector<bool> bit_pool(kPoolSize);
@@ -93,15 +113,9 @@
   std::minstd_rand0 rnd_engine(kRandomSeed);
 
   uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
 
-  BitVector bv;
-  for (uint32_t i = 0; i < size; ++i) {
-    if (rnd_engine() % 2) {
-      bv.AppendTrue();
-    } else {
-      bv.AppendFalse();
-    }
-  }
+  BitVector bv = BvWithSizeAndSetPercentage(size, set_percentage);
 
   static constexpr uint32_t kPoolSize = 1024 * 1024;
   std::vector<uint32_t> row_pool(kPoolSize);
@@ -123,19 +137,15 @@
   std::minstd_rand0 rnd_engine(kRandomSeed);
 
   uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
 
-  BitVector bv;
-  for (uint32_t i = 0; i < size; ++i) {
-    if (rnd_engine() % 2) {
-      bv.AppendTrue();
-    } else {
-      bv.AppendFalse();
-    }
-  }
-
+  BitVector bv = BvWithSizeAndSetPercentage(size, set_percentage);
   static constexpr uint32_t kPoolSize = 1024 * 1024;
   std::vector<uint32_t> row_pool(kPoolSize);
   uint32_t set_bit_count = bv.GetNumBitsSet();
+  if (set_bit_count == 0)
+    return;
+
   for (uint32_t i = 0; i < kPoolSize; ++i) {
     row_pool[i] = rnd_engine() % set_bit_count;
   }
@@ -153,11 +163,12 @@
   std::minstd_rand0 rnd_engine(kRandomSeed);
 
   uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
 
   uint32_t count = 0;
   BitVector bv;
   for (uint32_t i = 0; i < size; ++i) {
-    bool value = rnd_engine() % 2;
+    bool value = rnd_engine() % 100 < set_percentage;
     if (value) {
       bv.AppendTrue();
     } else {
@@ -200,16 +211,39 @@
 }
 BENCHMARK(BM_BitVectorResize);
 
+static void BM_BitVectorRangeFixedSize(benchmark::State& state) {
+  static constexpr uint32_t kRandomSeed = 42;
+  std::minstd_rand0 rnd_engine(kRandomSeed);
+
+  uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
+
+  std::vector<uint32_t> resize_fill_pool(size);
+  for (uint32_t i = 0; i < size; ++i) {
+    resize_fill_pool[i] = rnd_engine() % 100 < set_percentage ? 90 : 100;
+  }
+
+  for (auto _ : state) {
+    auto filler = [&resize_fill_pool](uint32_t i) PERFETTO_ALWAYS_INLINE {
+      return resize_fill_pool[i] < 95;
+    };
+    BitVector bv = BitVector::Range(0, size, filler);
+    benchmark::ClobberMemory();
+  }
+}
+BENCHMARK(BM_BitVectorRangeFixedSize)->Apply(BitVectorArgs);
+
 static void BM_BitVectorUpdateSetBits(benchmark::State& state) {
   static constexpr uint32_t kRandomSeed = 42;
   std::minstd_rand0 rnd_engine(kRandomSeed);
 
   uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
 
   BitVector bv;
   BitVector picker;
   for (uint32_t i = 0; i < size; ++i) {
-    bool value = rnd_engine() % 2;
+    bool value = rnd_engine() % 100 < set_percentage;
     if (value) {
       bv.AppendTrue();
 
@@ -234,3 +268,16 @@
   }
 }
 BENCHMARK(BM_BitVectorUpdateSetBits)->Apply(BitVectorArgs);
+
+static void BM_BitVectorSetBitsIterator(benchmark::State& state) {
+  uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
+
+  BitVector bv = BvWithSizeAndSetPercentage(size, set_percentage);
+  for (auto _ : state) {
+    for (auto it = bv.IterateSetBits(); it; it.Next()) {
+      benchmark::DoNotOptimize(it.index());
+    }
+  }
+}
+BENCHMARK(BM_BitVectorSetBitsIterator)->Apply(BitVectorArgs);
diff --git a/src/trace_processor/containers/bit_vector_iterators.h b/src/trace_processor/containers/bit_vector_iterators.h
index 0047812..62094ff 100644
--- a/src/trace_processor/containers/bit_vector_iterators.h
+++ b/src/trace_processor/containers/bit_vector_iterators.h
@@ -132,6 +132,16 @@
   // Increments the iterator to point to the next bit.
   void Next() { SetIndex(index() + 1); }
 
+  // Increments the iterator to skip the next |n| bits and point to the
+  // following one.
+  // Precondition: n >= 1 & index() + n <= size().
+  void Skip(uint32_t n) {
+    PERFETTO_DCHECK(n >= 1);
+    PERFETTO_DCHECK(index() + n <= size());
+
+    SetIndex(index() + n);
+  }
+
   // Returns whether the iterator is valid.
   operator bool() const { return index() < size(); }
 };
diff --git a/src/trace_processor/containers/bit_vector_unittest.cc b/src/trace_processor/containers/bit_vector_unittest.cc
index e185afd..4a78c2c 100644
--- a/src/trace_processor/containers/bit_vector_unittest.cc
+++ b/src/trace_processor/containers/bit_vector_unittest.cc
@@ -237,6 +237,22 @@
   ASSERT_TRUE(bv.IsSet(4));
 }
 
+TEST(BitVectorUnittest, UpdateSetBitsSmallerPicker) {
+  BitVector bv(6, false);
+  bv.Set(1);
+  bv.Set(2);
+  bv.Set(4);
+
+  BitVector picker(2u, true);
+  picker.Clear(1);
+
+  bv.UpdateSetBits(picker);
+
+  ASSERT_TRUE(bv.IsSet(1));
+  ASSERT_FALSE(bv.IsSet(2));
+  ASSERT_FALSE(bv.IsSet(4));
+}
+
 TEST(BitVectorUnittest, IterateAllBitsConst) {
   BitVector bv;
   for (uint32_t i = 0; i < 12345; ++i) {
@@ -383,6 +399,18 @@
   ASSERT_FALSE(it);
 }
 
+TEST(BitVectorUnittest, Range) {
+  BitVector bv =
+      BitVector::Range(1, 1025, [](uint32_t t) { return t % 3 == 0; });
+
+  ASSERT_FALSE(bv.IsSet(0));
+  for (uint32_t i = 1; i < 1025; ++i) {
+    ASSERT_EQ(i % 3 == 0, bv.IsSet(i));
+  }
+  ASSERT_EQ(bv.size(), 1025u);
+  ASSERT_EQ(bv.GetNumBitsSet(), 341u);
+}
+
 TEST(BitVectorUnittest, QueryStressTest) {
   BitVector bv;
   std::vector<bool> bool_vec;
diff --git a/src/trace_processor/containers/row_map.cc b/src/trace_processor/containers/row_map.cc
index b10d0cb..0a3def0 100644
--- a/src/trace_processor/containers/row_map.cc
+++ b/src/trace_processor/containers/row_map.cc
@@ -36,9 +36,9 @@
                          uint32_t end,
                          const BitVector& selector) {
   PERFETTO_DCHECK(start <= end);
-  PERFETTO_DCHECK(end - start == selector.size());
+  PERFETTO_DCHECK(selector.size() <= end - start);
 
-  // If |start| == 0 and |end - start| == |selector.size()| (which is a
+  // If |start| == 0 and |selector.size()| <= |end - start| (which is a
   // precondition for this function), the BitVector we generate is going to be
   // exactly |selector|.
   //
@@ -49,8 +49,11 @@
   if (start == 0u)
     return RowMap(selector.Copy());
 
+  // We only need to resize to |start| + |selector.size()| as we know any rows
+  // not covered by |selector| are going to be removed below.
   BitVector bv(start, false);
-  bv.Resize(end, true);
+  bv.Resize(start + selector.size(), true);
+
   bv.UpdateSetBits(selector);
   return RowMap(std::move(bv));
 }
diff --git a/src/trace_processor/containers/row_map.h b/src/trace_processor/containers/row_map.h
index cac8f2e..8cf91f1 100644
--- a/src/trace_processor/containers/row_map.h
+++ b/src/trace_processor/containers/row_map.h
@@ -242,16 +242,19 @@
     PERFETTO_FATAL("For GCC");
   }
 
+  // Returns whether this rowmap is empty.
+  bool empty() const { return size() == 0; }
+
   // Returns the row at index |row|.
   uint32_t Get(uint32_t idx) const {
     PERFETTO_DCHECK(idx < size());
     switch (mode_) {
       case Mode::kRange:
-        return start_idx_ + idx;
+        return GetRange(idx);
       case Mode::kBitVector:
-        return bit_vector_.IndexOfNthSet(idx);
+        return GetBitVector(idx);
       case Mode::kIndexVector:
-        return index_vector_[idx];
+        return GetIndexVector(idx);
     }
     PERFETTO_FATAL("For GCC");
   }
@@ -392,7 +395,7 @@
     }
 
     // TODO(lalitm): improve efficiency of this if we end up needing it.
-    RemoveIf([&other](uint32_t row) { return !other.Contains(row); });
+    Filter([&other](uint32_t row) { return other.Contains(row); });
   }
 
   // Filters the current RowMap into the RowMap given by |out| based on the
@@ -421,7 +424,7 @@
   void FilterInto(RowMap* out, Predicate p) const {
     PERFETTO_DCHECK(size() >= out->size());
 
-    if (out->size() == 0) {
+    if (out->empty()) {
       // If the output RowMap is empty, we don't need to do anything.
       return;
     }
@@ -438,41 +441,52 @@
     // cases where |out| has only a few entries so we can scan |out| instead of
     // scanning |this|.
 
-    // TODO(lalit): investigate whether we should also scan |out| if |this| is
-    // a range or index vector as, in those cases, it would be fast to lookup
-    // |this| by index.
-
-    // We choose to full scan |this| rather than |out| as the performance
-    // penalty of incorrectly scanning |out| is much worse than mistakely
-    // scanning |this|.
-    // This is because scans on |out| involve an indexed lookup on |this| which
-    // (in the case of a bitvector) can be very expensive. On the other hand,
-    // scanning |this| means we never have to do indexed lookups but we may
-    // scan many more rows than necessary (as they may have already been
-    // excluded in out).
-    FilterIntoScanSelf(out, p);
-  }
-
-  template <typename Comparator>
-  void StableSort(std::vector<uint32_t>* out, Comparator c) const {
+    // Ideally, we'd always just scan the rows in |out| and keep those which
+    // meet |p|. However, if |this| is a BitVector, we end up needing expensive
+    // |IndexOfNthSet| calls (as we need to lookup the row before passing it to
+    // |p|).
     switch (mode_) {
       case Mode::kRange: {
-        StableSort(out, c, [this](uint32_t off) { return start_idx_ + off; });
+        auto ip = [this, p](uint32_t idx) { return p(GetRange(idx)); };
+        out->Filter(ip);
         break;
       }
       case Mode::kBitVector: {
-        StableSort(out, c, [this](uint32_t off) {
-          return bit_vector_.IndexOfNthSet(off);
-        });
+        FilterIntoScanSelfBv(out, p);
         break;
       }
       case Mode::kIndexVector: {
-        StableSort(out, c, [this](uint32_t off) { return index_vector_[off]; });
+        auto ip = [this, p](uint32_t row) { return p(GetIndexVector(row)); };
+        out->Filter(ip);
         break;
       }
     }
   }
 
+  template <typename Comparator = bool(uint32_t, uint32_t)>
+  void StableSort(std::vector<uint32_t>* out, Comparator c) const {
+    switch (mode_) {
+      case Mode::kRange:
+        std::stable_sort(out->begin(), out->end(),
+                         [this, c](uint32_t a, uint32_t b) {
+                           return c(GetRange(a), GetRange(b));
+                         });
+        break;
+      case Mode::kBitVector:
+        std::stable_sort(out->begin(), out->end(),
+                         [this, c](uint32_t a, uint32_t b) {
+                           return c(GetBitVector(a), GetBitVector(b));
+                         });
+        break;
+      case Mode::kIndexVector:
+        std::stable_sort(out->begin(), out->end(),
+                         [this, c](uint32_t a, uint32_t b) {
+                           return c(GetIndexVector(a), GetIndexVector(b));
+                         });
+        break;
+    }
+  }
+
   // Returns the iterator over the rows in this RowMap.
   Iterator IterateRows() const { return Iterator(this); }
 
@@ -486,29 +500,35 @@
     kIndexVector,
   };
 
-  // Filters the current RowMap into |out| by performing a full scan on |this|.
-  // See |FilterInto| for a full breakdown of the semantics of this function.
+  // Filters the indices in |out| by keeping those which meet |p|.
   template <typename Predicate>
-  void FilterIntoScanSelf(RowMap* out, Predicate p) const {
+  void Filter(Predicate p) {
     switch (mode_) {
       case Mode::kRange:
-        FilterIntoScanSelf(out, RangeIterator(this), p);
+        FilterRange(p);
         break;
-      case Mode::kBitVector:
-        FilterIntoScanSelf(out, bit_vector_.IterateSetBits(), p);
+      case Mode::kBitVector: {
+        for (auto it = bit_vector_.IterateSetBits(); it; it.Next()) {
+          if (!p(it.index()))
+            it.Clear();
+        }
         break;
-      case Mode::kIndexVector:
-        FilterIntoScanSelf(out, IndexVectorIterator(this), p);
+      }
+      case Mode::kIndexVector: {
+        auto ret = std::remove_if(index_vector_.begin(), index_vector_.end(),
+                                  [p](uint32_t i) { return !p(i); });
+        index_vector_.erase(ret, index_vector_.end());
         break;
+      }
     }
   }
 
   // Filters the current RowMap into |out| by performing a full scan on |this|
-  // using the |it|, a strongly typed iterator on |this| (a strongly typed
-  // iterator is used for performance reasons).
+  // where |this| is a BitVector.
   // See |FilterInto| for a full breakdown of the semantics of this function.
-  template <typename Iterator, typename Predicate>
-  void FilterIntoScanSelf(RowMap* out, Iterator it, Predicate p) const {
+  template <typename Predicate>
+  void FilterIntoScanSelfBv(RowMap* out, Predicate p) const {
+    auto it = bit_vector_.IterateSetBits();
     switch (out->mode_) {
       case Mode::kRange: {
         // TODO(lalitm): investigate whether we can reuse the data inside
@@ -556,6 +576,53 @@
     }
   }
 
+  template <typename Predicate>
+  void FilterRange(Predicate p) {
+    uint32_t count = end_idx_ - start_idx_;
+
+    // Optimization: if we are only going to scan a few rows, it's not
+    // worth the haslle of working with a BitVector.
+    constexpr uint32_t kSmallRangeLimit = 2048;
+    bool is_small_range = count < kSmallRangeLimit;
+
+    // Optimization: weif the cost of a BitVector is more than the highest
+    // possible cost an index vector could have, use the index vector.
+    uint32_t bit_vector_cost = BitVector::ApproxBytesCost(end_idx_);
+    uint32_t index_vector_cost_ub = sizeof(uint32_t) * count;
+
+    // If either of the conditions hold which make it better to use an
+    // index vector, use it instead.
+    if (is_small_range || index_vector_cost_ub <= bit_vector_cost) {
+      // Try and strike a good balance between not making the vector too
+      // big and good performance.
+      std::vector<uint32_t> iv(std::min(kSmallRangeLimit, count));
+
+      uint32_t out_idx = 0;
+      for (uint32_t i = 0; i < count; ++i) {
+        // If we reach the capacity add another small set of indices.
+        if (PERFETTO_UNLIKELY(out_idx == iv.size()))
+          iv.resize(iv.size() + kSmallRangeLimit);
+
+        // We keep this branch free by always writing the index but only
+        // incrementing the out index if the return value is true.
+        bool value = p(i + start_idx_);
+        iv[out_idx] = i + start_idx_;
+        out_idx += value;
+      }
+
+      // Make the vector the correct size and as small as possible.
+      iv.resize(out_idx);
+      iv.shrink_to_fit();
+
+      *this = RowMap(std::move(iv));
+      return;
+    }
+
+    // Otherwise, create a bitvector which spans the full range using
+    // |p| as the filler for the bits between start and end.
+    *this = RowMap(BitVector::Range(start_idx_, end_idx_, p));
+  }
+
   void InsertIntoBitVector(uint32_t row) {
     PERFETTO_DCHECK(mode_ == Mode::kBitVector);
 
@@ -564,41 +631,17 @@
     bit_vector_.Set(row);
   }
 
-  // Removes any row where |p(row)| returns false from this RowMap.
-  template <typename Predicate>
-  void RemoveIf(Predicate p) {
-    switch (mode_) {
-      case Mode::kRange: {
-        bit_vector_.Resize(start_idx_, false);
-        for (uint32_t i = start_idx_; i < end_idx_; ++i) {
-          if (p(i))
-            bit_vector_.AppendFalse();
-          else
-            bit_vector_.AppendTrue();
-        }
-        *this = RowMap(std::move(bit_vector_));
-        break;
-      }
-      case Mode::kBitVector: {
-        for (auto it = bit_vector_.IterateSetBits(); it; it.Next()) {
-          if (p(it.index()))
-            it.Clear();
-        }
-        break;
-      }
-      case Mode::kIndexVector: {
-        auto it = std::remove_if(index_vector_.begin(), index_vector_.end(), p);
-        index_vector_.erase(it, index_vector_.end());
-        break;
-      }
-    }
+  PERFETTO_ALWAYS_INLINE uint32_t GetRange(uint32_t idx) const {
+    PERFETTO_DCHECK(mode_ == Mode::kRange);
+    return start_idx_ + idx;
   }
-
-  template <typename Comparator, typename Indexer>
-  void StableSort(std::vector<uint32_t>* out, Comparator c, Indexer i) const {
-    std::stable_sort(
-        out->begin(), out->end(),
-        [&c, &i](uint32_t a, uint32_t b) { return c(i(a), i(b)); });
+  PERFETTO_ALWAYS_INLINE uint32_t GetBitVector(uint32_t idx) const {
+    PERFETTO_DCHECK(mode_ == Mode::kBitVector);
+    return bit_vector_.IndexOfNthSet(idx);
+  }
+  PERFETTO_ALWAYS_INLINE uint32_t GetIndexVector(uint32_t idx) const {
+    PERFETTO_DCHECK(mode_ == Mode::kIndexVector);
+    return index_vector_[idx];
   }
 
   RowMap SelectRowsSlow(const RowMap& selector) const;
diff --git a/src/trace_processor/containers/row_map_unittest.cc b/src/trace_processor/containers/row_map_unittest.cc
index c85b5fe..fde10d5 100644
--- a/src/trace_processor/containers/row_map_unittest.cc
+++ b/src/trace_processor/containers/row_map_unittest.cc
@@ -195,6 +195,15 @@
   ASSERT_EQ(res.Get(1u), 30u);
 }
 
+TEST(RowMapUnittest, SelectRangeWithSmallBitVector) {
+  RowMap rm(27, 31);
+  RowMap picker(BitVector{false, true});
+  auto res = rm.SelectRows(picker);
+
+  ASSERT_EQ(res.size(), 1u);
+  ASSERT_EQ(res.Get(0u), 28u);
+}
+
 TEST(RowMapUnittest, SelectBitVectorWithBitVector) {
   RowMap rm(BitVector{true, false, true, true, false, true});
   RowMap picker(BitVector{true, false, false, true});
@@ -205,6 +214,15 @@
   ASSERT_EQ(res.Get(1u), 5u);
 }
 
+TEST(RowMapUnittest, SelectBitVectorWithSmallBitVector) {
+  RowMap rm(BitVector{true, false, true, true, false, true});
+  RowMap picker(BitVector{false, true});
+  auto res = rm.SelectRows(picker);
+
+  ASSERT_EQ(res.size(), 1u);
+  ASSERT_EQ(res.Get(0u), 2u);
+}
+
 TEST(RowMapUnittest, SelectIndexVectorWithBitVector) {
   RowMap rm(std::vector<uint32_t>{0u, 2u, 3u, 5u});
   RowMap picker(BitVector{true, false, false, true});
@@ -330,6 +348,26 @@
   ASSERT_EQ(filter.Get(1u), 5u);
 }
 
+TEST(RowMapUnittest, FilterIntoOffsetRangeWithRange) {
+  RowMap rm(100000, 100010);
+  RowMap filter(4, 7);
+  rm.FilterInto(&filter, [](uint32_t row) { return row == 100004u; });
+
+  ASSERT_EQ(filter.size(), 1u);
+  ASSERT_EQ(filter.Get(0u), 4u);
+}
+
+TEST(RowMapUnittest, FilterIntoLargeRangeWithRange) {
+  RowMap rm(0, 100000);
+  RowMap filter(0, 100000);
+  rm.FilterInto(&filter, [](uint32_t row) { return row % 2 == 0; });
+
+  ASSERT_EQ(filter.size(), 100000u / 2);
+  for (uint32_t i = 0; i < 100000 / 2; ++i) {
+    ASSERT_EQ(filter.Get(i), i * 2);
+  }
+}
+
 TEST(RowMapUnittest, FilterIntoBitVectorWithRange) {
   RowMap rm(
       BitVector{true, false, false, true, false, true, false, true, true});
diff --git a/src/trace_processor/containers/string_pool.cc b/src/trace_processor/containers/string_pool.cc
index 020de1c..fd65195 100644
--- a/src/trace_processor/containers/string_pool.cc
+++ b/src/trace_processor/containers/string_pool.cc
@@ -24,61 +24,90 @@
 namespace perfetto {
 namespace trace_processor {
 
-StringPool::StringPool(size_t block_size_bytes)
-    : block_size_bytes_(block_size_bytes > 0 ? block_size_bytes
-                                             : kDefaultBlockSize) {
-  blocks_.emplace_back(block_size_bytes_);
+// static
+constexpr size_t StringPool::kNumBlockIndexBits;
+// static
+constexpr size_t StringPool::kNumBlockOffsetBits;
+// static
+constexpr size_t StringPool::kLargeStringFlagBitMask;
+// static
+constexpr size_t StringPool::kBlockOffsetBitMask;
+// static
+constexpr size_t StringPool::kBlockIndexBitMask;
+// static
+constexpr size_t StringPool::kBlockSizeBytes;
+// static
+constexpr size_t StringPool::kMinLargeStringSizeBytes;
+
+StringPool::StringPool() {
+  static_assert(
+      StringPool::kMinLargeStringSizeBytes <= StringPool::kBlockSizeBytes + 1,
+      "minimum size of large strings must be small enough to support any "
+      "string that doesn't fit in a Block.");
+
+  blocks_.emplace_back(kBlockSizeBytes);
 
   // Reserve a slot for the null string.
-  PERFETTO_CHECK(blocks_.back().TryInsert(NullTermStringView()));
+  PERFETTO_CHECK(blocks_.back().TryInsert(NullTermStringView()).first);
 }
 
 StringPool::~StringPool() = default;
 
-StringPool::StringPool(StringPool&&) noexcept = default;
+StringPool::StringPool(StringPool&&) = default;
 StringPool& StringPool::operator=(StringPool&&) = default;
 
 StringPool::Id StringPool::InsertString(base::StringView str, uint64_t hash) {
   // Try and find enough space in the current block for the string and the
   // metadata (varint-encoded size + the string data + the null terminator).
-  const uint8_t* ptr = blocks_.back().TryInsert(str);
-  if (PERFETTO_UNLIKELY(!ptr)) {
-    // This means the block did not have enough space. This should only happen
-    // if the block size is small.
-    PERFETTO_CHECK(block_size_bytes_ <= std::numeric_limits<uint32_t>::max());
-
-    // Add a new block to store the data. If the string is larger that the
-    // default block size, add a bigger block exlusively for this string.
-    if (str.size() + kMaxMetadataSize > block_size_bytes_) {
-      blocks_.emplace_back(str.size() +
-                           base::AlignUp<base::kPageSize>(kMaxMetadataSize));
+  bool success;
+  uint32_t offset;
+  std::tie(success, offset) = blocks_.back().TryInsert(str);
+  if (PERFETTO_UNLIKELY(!success)) {
+    // The block did not have enough space for the string. If the string is
+    // large, add it into the |large_strings_| vector, to avoid discarding a
+    // large portion of the current block's memory. This also enables us to
+    // support strings that wouldn't fit into a single block. Otherwise, add a
+    // new block to store the string.
+    if (str.size() + kMaxMetadataSize >= kMinLargeStringSizeBytes) {
+      return InsertLargeString(str, hash);
     } else {
-      blocks_.emplace_back(block_size_bytes_);
+      blocks_.emplace_back(kBlockSizeBytes);
     }
 
     // Try and reserve space again - this time we should definitely succeed.
-    ptr = blocks_.back().TryInsert(str);
-    PERFETTO_CHECK(ptr);
+    std::tie(success, offset) = blocks_.back().TryInsert(str);
+    PERFETTO_CHECK(success);
   }
 
-  // Finish by computing the id of the pointer and adding a mapping from the
-  // hash to the string_id.
-  Id string_id = PtrToId(ptr);
+  // Compute the id from the block index and offset and add a mapping from the
+  // hash to the id.
+  Id string_id = Id::BlockString(blocks_.size() - 1, offset);
   string_index_.emplace(hash, string_id);
   return string_id;
 }
 
-const uint8_t* StringPool::Block::TryInsert(base::StringView str) {
+StringPool::Id StringPool::InsertLargeString(base::StringView str,
+                                             uint64_t hash) {
+  large_strings_.emplace_back(new std::string(str.begin(), str.size()));
+  // Compute id from the index and add a mapping from the hash to the id.
+  Id string_id = Id::LargeString(large_strings_.size() - 1);
+  string_index_.emplace(hash, string_id);
+  return string_id;
+}
+
+std::pair<bool /*success*/, uint32_t /*offset*/> StringPool::Block::TryInsert(
+    base::StringView str) {
   auto str_size = str.size();
   size_t max_pos = static_cast<size_t>(pos_) + str_size + kMaxMetadataSize;
   if (max_pos > size_)
-    return nullptr;
+    return std::make_pair(false, 0u);
 
   // Ensure that we commit up until the end of the string to memory.
   mem_.EnsureCommitted(max_pos);
 
   // Get where we should start writing this string.
-  uint8_t* begin = Get(pos_);
+  uint32_t offset = pos_;
+  uint8_t* begin = Get(offset);
 
   // First write the size of the string using varint encoding.
   uint8_t* end = protozero::proto_utils::WriteVarInt(str_size, begin);
@@ -95,55 +124,59 @@
   // Update the end of the block and return the pointer to the string.
   pos_ = OffsetOf(end);
 
-  return begin;
+  return std::make_pair(true, offset);
 }
 
 StringPool::Iterator::Iterator(const StringPool* pool) : pool_(pool) {}
 
 StringPool::Iterator& StringPool::Iterator::operator++() {
-  PERFETTO_DCHECK(block_id_ < pool_->blocks_.size());
+  if (block_index_ < pool_->blocks_.size()) {
+    // Try and go to the next string in the current block.
+    const auto& block = pool_->blocks_[block_index_];
 
-  // Try and go to the next string in the current block.
-  const auto& block = pool_->blocks_[block_id_];
+    // Find the size of the string at the current offset in the block
+    // and increment the offset by that size.
+    uint32_t str_size = 0;
+    const uint8_t* ptr = block.Get(block_offset_);
+    ptr = ReadSize(ptr, &str_size);
+    ptr += str_size + 1;
+    block_offset_ = block.OffsetOf(ptr);
 
-  // Find the size of the string at the current offset in the block
-  // and increment the offset by that size.
-  uint32_t str_size = 0;
-  const uint8_t* ptr = block.Get(block_offset_);
-  ptr = ReadSize(ptr, &str_size);
-  ptr += str_size + 1;
-  block_offset_ = block.OffsetOf(ptr);
+    // If we're out of bounds for this block, go to the start of the next block.
+    if (block.pos() <= block_offset_) {
+      block_index_++;
+      block_offset_ = 0;
+    }
 
-  // If we're out of bounds for this block, go to the start of the next block.
-  if (block.pos() <= block_offset_) {
-    block_id_++;
-    block_offset_ = 0;
+    return *this;
   }
+
+  // Advance to the next string from |large_strings_|.
+  PERFETTO_DCHECK(large_strings_index_ < pool_->large_strings_.size());
+  large_strings_index_++;
   return *this;
 }
 
 StringPool::Iterator::operator bool() const {
-  return block_id_ < pool_->blocks_.size();
+  return block_index_ < pool_->blocks_.size() ||
+         large_strings_index_ < pool_->large_strings_.size();
 }
 
 NullTermStringView StringPool::Iterator::StringView() {
-  PERFETTO_DCHECK(block_id_ < pool_->blocks_.size());
-  PERFETTO_DCHECK(block_offset_ < pool_->blocks_[block_id_].pos());
-
-  // If we're at (0, 0), we have the null string.
-  if (block_id_ == 0 && block_offset_ == 0)
-    return NullTermStringView();
-  return GetFromPtr(pool_->blocks_[block_id_].Get(block_offset_));
+  return pool_->Get(StringId());
 }
 
 StringPool::Id StringPool::Iterator::StringId() {
-  PERFETTO_DCHECK(block_id_ < pool_->blocks_.size());
-  PERFETTO_DCHECK(block_offset_ < pool_->blocks_[block_id_].pos());
+  if (block_index_ < pool_->blocks_.size()) {
+    PERFETTO_DCHECK(block_offset_ < pool_->blocks_[block_index_].pos());
 
-  // If we're at (0, 0), we have the null string which has id 0.
-  if (block_id_ == 0 && block_offset_ == 0)
-    return 0;
-  return pool_->PtrToId(pool_->blocks_[block_id_].Get(block_offset_));
+    // If we're at (0, 0), we have the null string which has id 0.
+    if (block_index_ == 0 && block_offset_ == 0)
+      return Id::Null();
+    return Id::BlockString(block_index_, block_offset_);
+  }
+  PERFETTO_DCHECK(large_strings_index_ < pool_->large_strings_.size());
+  return Id::LargeString(large_strings_index_);
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/containers/string_pool.h b/src/trace_processor/containers/string_pool.h
index bdfc39f..11ae91c 100644
--- a/src/trace_processor/containers/string_pool.h
+++ b/src/trace_processor/containers/string_pool.h
@@ -31,22 +31,12 @@
 namespace perfetto {
 namespace trace_processor {
 
-// On 64-bit platforms, the string pool is implemented as a mmaped buffer
-// of 4GB with the id being equal ot the offset into this buffer of the string.
-// On 32-bit platforms instead, the implementation allocates 32MB blocks of
-// mmaped memory with the pointer being directly converted to the id.
-constexpr size_t kDefaultBlockSize =
-    sizeof(void*) == 8
-        ? static_cast<size_t>(4ull * 1024ull * 1024ull * 1024ull) /* 4GB */
-        : 32ull * 1024ull * 1024ull /* 32MB */;
-
 // Interns strings in a string pool and hands out compact StringIds which can
 // be used to retrieve the string in O(1).
 class StringPool {
  public:
   struct Id {
     Id() = default;
-    constexpr Id(uint32_t i) : id(i) {}
 
     bool operator==(const Id& other) const { return other.id == id; }
     bool operator!=(const Id& other) const { return !(other == *this); }
@@ -54,6 +44,42 @@
 
     bool is_null() const { return id == 0u; }
 
+    bool is_large_string() const { return id & kLargeStringFlagBitMask; }
+
+    uint32_t block_offset() const { return id & kBlockOffsetBitMask; }
+
+    uint32_t block_index() const {
+      return (id & kBlockIndexBitMask) >> kNumBlockOffsetBits;
+    }
+
+    uint32_t large_string_index() const {
+      PERFETTO_DCHECK(is_large_string());
+      return id & ~kLargeStringFlagBitMask;
+    }
+
+    uint32_t raw_id() const { return id; }
+
+    static Id LargeString(size_t index) {
+      PERFETTO_DCHECK(index <= static_cast<uint32_t>(index));
+      PERFETTO_DCHECK(!(index & kLargeStringFlagBitMask));
+      return Id(kLargeStringFlagBitMask | static_cast<uint32_t>(index));
+    }
+
+    static Id BlockString(size_t index, uint32_t offset) {
+      PERFETTO_DCHECK(index < (1u << (kNumBlockIndexBits + 1)));
+      PERFETTO_DCHECK(offset < (1u << (kNumBlockOffsetBits + 1)));
+      return Id(~kLargeStringFlagBitMask &
+                (static_cast<uint32_t>(index << kNumBlockOffsetBits) |
+                 (offset & kBlockOffsetBitMask)));
+    }
+
+    static constexpr Id Raw(uint32_t raw) { return Id(raw); }
+
+    static constexpr Id Null() { return Id(0u); }
+
+   private:
+    constexpr Id(uint32_t i) : id(i) {}
+
     uint32_t id;
   };
 
@@ -70,15 +96,16 @@
 
    private:
     const StringPool* pool_ = nullptr;
-    uint32_t block_id_ = 0;
+    uint32_t block_index_ = 0;
     uint32_t block_offset_ = 0;
+    uint32_t large_strings_index_ = 0;
   };
 
-  StringPool(size_t block_size_bytes = kDefaultBlockSize);
+  StringPool();
   ~StringPool();
 
   // Allow std::move().
-  StringPool(StringPool&&) noexcept;
+  StringPool(StringPool&&);
   StringPool& operator=(StringPool&&);
 
   // Disable implicit copy.
@@ -87,7 +114,7 @@
 
   Id InternString(base::StringView str) {
     if (str.data() == nullptr)
-      return Id(0);
+      return Id::Null();
 
     auto hash = str.Hash();
     auto id_it = string_index_.find(hash);
@@ -100,7 +127,7 @@
 
   base::Optional<Id> GetId(base::StringView str) const {
     if (str.data() == nullptr)
-      return Id(0u);
+      return Id::Null();
 
     auto hash = str.Hash();
     auto id_it = string_index_.find(hash);
@@ -112,9 +139,11 @@
   }
 
   NullTermStringView Get(Id id) const {
-    if (id.id == 0)
+    if (id.is_null())
       return NullTermStringView();
-    return GetFromPtr(IdToPtr(id));
+    if (id.is_large_string())
+      return GetLargeString(id);
+    return GetFromBlockPtr(IdToPtr(id));
   }
 
   Iterator CreateIterator() const { return Iterator(this); }
@@ -143,11 +172,12 @@
       return static_cast<uint8_t*>(mem_.Get()) + offset;
     }
 
-    const uint8_t* TryInsert(base::StringView str);
+    std::pair<bool /*success*/, uint32_t /*offset*/> TryInsert(
+        base::StringView str);
 
     uint32_t OffsetOf(const uint8_t* ptr) const {
       PERFETTO_DCHECK(Get(0) < ptr &&
-                      ptr < Get(static_cast<uint32_t>(size_ - 1)));
+                      ptr <= Get(static_cast<uint32_t>(size_ - 1)));
       return static_cast<uint32_t>(ptr - Get(0));
     }
 
@@ -160,43 +190,59 @@
   };
 
   friend class Iterator;
+  friend class StringPoolTest;
+
+  // StringPool IDs are 32-bit. If the MSB is 1, the remaining bits of the ID
+  // are an index into the |large_strings_| vector. Otherwise, the next 6 bits
+  // are the index of the Block in the pool, and the remaining 25 bits the
+  // offset of the encoded string inside the pool.
+  //
+  // [31] [30:25] [24:0]
+  //  |      |       |
+  //  |      |       +---- offset in block (or LSB of large string index).
+  //  |      +------------ block index (or MSB of large string index).
+  //  +------------------- 1: large string, 0: string in a Block.
+  static constexpr size_t kNumBlockIndexBits = 6;
+  static constexpr size_t kNumBlockOffsetBits = 25;
+
+  static constexpr size_t kLargeStringFlagBitMask = 1u << 31;
+  static constexpr size_t kBlockOffsetBitMask = (1u << kNumBlockOffsetBits) - 1;
+  static constexpr size_t kBlockIndexBitMask =
+      0xffffffff & ~kLargeStringFlagBitMask & ~kBlockOffsetBitMask;
+
+  static constexpr size_t kBlockSizeBytes = kBlockOffsetBitMask + 1;  // 32 MB
+
+  // If a string doesn't fit into the current block, we can either start a new
+  // block or insert the string into the |large_strings_| vector. To maximize
+  // the used proportion of each block's memory, we only start a new block if
+  // the string isn't very large.
+  static constexpr size_t kMinLargeStringSizeBytes = kBlockSizeBytes / 8;
 
   // Number of bytes to reserve for size and null terminator.
   // This is the upper limit on metadata size: 5 bytes for max uint32,
   // plus 1 byte for null terminator. The actual size may be lower.
   static constexpr uint8_t kMaxMetadataSize = 6;
 
-  // Inserts the string with the given hash into the pool
+  // Inserts the string with the given hash into the pool and return its Id.
   Id InsertString(base::StringView, uint64_t hash);
 
-  // |ptr| should point to the start of the string metadata (i.e. the first byte
-  // of the size).
-  Id PtrToId(const uint8_t* ptr) const {
-    // For a 64 bit architecture, the id is the offset of the pointer inside
-    // the one and only 4GB block.
-    if (sizeof(void*) == 8) {
-      PERFETTO_DCHECK(blocks_.size() == 1);
-      return Id(blocks_.back().OffsetOf(ptr));
-    }
-
-    // On 32 bit architectures, the size of the pointer is 32-bit so we simply
-    // use the pointer itself as the id.
-    // Double cast needed because, on 64 archs, the compiler complains that we
-    // are losing information.
-    return Id(static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr)));
-  }
+  // Insert a large string into the pool and return its Id.
+  Id InsertLargeString(base::StringView, uint64_t hash);
 
   // The returned pointer points to the start of the string metadata (i.e. the
   // first byte of the size).
   const uint8_t* IdToPtr(Id id) const {
-    // For a 64 bit architecture, the pointer is simply the found by taking
-    // the base of the 4GB block and adding the offset given by |id|.
-    if (sizeof(void*) == 8) {
-      PERFETTO_DCHECK(blocks_.size() == 1);
-      return blocks_.back().Get(id.id);
-    }
-    // On a 32 bit architecture, the pointer is the same as the id.
-    return reinterpret_cast<uint8_t*>(id.id);
+    // If the MSB is set, the ID represents an index into |large_strings_|, so
+    // shouldn't be converted into a block pointer.
+    PERFETTO_DCHECK(!id.is_large_string());
+
+    size_t block_index = id.block_index();
+    uint32_t block_offset = id.block_offset();
+
+    PERFETTO_DCHECK(block_index < blocks_.size());
+    PERFETTO_DCHECK(block_offset < blocks_[block_index].pos());
+
+    return blocks_[block_index].Get(block_offset);
   }
 
   // |ptr| should point to the start of the string metadata (i.e. the first byte
@@ -214,19 +260,30 @@
 
   // |ptr| should point to the start of the string metadata (i.e. the first byte
   // of the size).
-  static NullTermStringView GetFromPtr(const uint8_t* ptr) {
+  static NullTermStringView GetFromBlockPtr(const uint8_t* ptr) {
     uint32_t size = 0;
     const uint8_t* str_ptr = ReadSize(ptr, &size);
     return NullTermStringView(reinterpret_cast<const char*>(str_ptr), size);
   }
 
-  // The minimum size of a new block. A larger block may be created if a string
-  // is added that is larger than this size.
-  size_t block_size_bytes_;
+  // Lookup a string in the |large_strings_| vector. |id| should have the MSB
+  // set.
+  NullTermStringView GetLargeString(Id id) const {
+    PERFETTO_DCHECK(id.is_large_string());
+    size_t index = id.large_string_index();
+    PERFETTO_DCHECK(index < large_strings_.size());
+    const std::string* str = large_strings_[index].get();
+    return NullTermStringView(str->c_str(), str->size());
+  }
 
   // The actual memory storing the strings.
   std::vector<Block> blocks_;
 
+  // Any string that is too large to fit into a Block is stored separately
+  // (inside a unique_ptr to ensure any references to it remain valid even if
+  // |large_strings_| is resized).
+  std::vector<std::unique_ptr<std::string>> large_strings_;
+
   // Maps hashes of strings to the Id in the string pool.
   // TODO(lalitm): At some point we should benchmark just using a static
   // hashtable of 1M elements, we can afford paying a fixed 8MB here
@@ -244,7 +301,7 @@
   using result_type = size_t;
 
   result_type operator()(const argument_type& r) const {
-    return std::hash<uint32_t>{}(r.id);
+    return std::hash<uint32_t>{}(r.raw_id());
   }
 };
 
diff --git a/src/trace_processor/containers/string_pool_unittest.cc b/src/trace_processor/containers/string_pool_unittest.cc
index 0144d7c..eb6dc1b 100644
--- a/src/trace_processor/containers/string_pool_unittest.cc
+++ b/src/trace_processor/containers/string_pool_unittest.cc
@@ -16,66 +16,70 @@
 
 #include "src/trace_processor/containers/string_pool.h"
 
+#include <array>
 #include <random>
 
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
 namespace trace_processor {
+
+class StringPoolTest : public testing::Test {
+ protected:
+  static constexpr size_t kNumBlockOffsetBits = StringPool::kNumBlockOffsetBits;
+  static constexpr size_t kBlockIndexBitMask = StringPool::kBlockIndexBitMask;
+  static constexpr size_t kBlockSizeBytes = StringPool::kBlockSizeBytes;
+  static constexpr size_t kMinLargeStringSizeBytes =
+      StringPool::kMinLargeStringSizeBytes;
+
+  StringPool pool_;
+};
+
 namespace {
 
-TEST(StringPoolTest, EmptyPool) {
-  StringPool pool;
+TEST_F(StringPoolTest, EmptyPool) {
+  ASSERT_EQ(pool_.Get(StringPool::Id::Null()).c_str(), nullptr);
 
-  ASSERT_EQ(pool.Get(0).c_str(), nullptr);
-
-  auto it = pool.CreateIterator();
+  auto it = pool_.CreateIterator();
   ASSERT_TRUE(it);
   ASSERT_EQ(it.StringView().c_str(), nullptr);
   ASSERT_FALSE(++it);
 }
 
-TEST(StringPoolTest, InternAndRetrieve) {
-  StringPool pool;
-
+TEST_F(StringPoolTest, InternAndRetrieve) {
   static char kString[] = "Test String";
-  auto id = pool.InternString(kString);
-  ASSERT_STREQ(pool.Get(id).c_str(), kString);
-  ASSERT_EQ(pool.Get(id), kString);
-  ASSERT_EQ(id, pool.InternString(kString));
+  auto id = pool_.InternString(kString);
+  ASSERT_STREQ(pool_.Get(id).c_str(), kString);
+  ASSERT_EQ(pool_.Get(id), kString);
+  ASSERT_EQ(id, pool_.InternString(kString));
 }
 
-TEST(StringPoolTest, NullPointerHandling) {
-  StringPool pool;
-
-  auto id = pool.InternString(NullTermStringView());
-  ASSERT_EQ(id, 0u);
-  ASSERT_EQ(pool.Get(id).c_str(), nullptr);
+TEST_F(StringPoolTest, NullPointerHandling) {
+  auto id = pool_.InternString(NullTermStringView());
+  ASSERT_TRUE(id.is_null());
+  ASSERT_EQ(pool_.Get(id).c_str(), nullptr);
 }
 
-TEST(StringPoolTest, Iterator) {
-  StringPool pool;
-
-  auto it = pool.CreateIterator();
+TEST_F(StringPoolTest, Iterator) {
+  auto it = pool_.CreateIterator();
   ASSERT_TRUE(it);
   ASSERT_EQ(it.StringView().c_str(), nullptr);
   ASSERT_FALSE(++it);
 
   static char kString[] = "Test String";
-  pool.InternString(kString);
+  pool_.InternString(kString);
 
-  it = pool.CreateIterator();
+  it = pool_.CreateIterator();
   ASSERT_TRUE(++it);
   ASSERT_STREQ(it.StringView().c_str(), kString);
   ASSERT_FALSE(++it);
 }
 
-TEST(StringPoolTest, ConstIterator) {
-  StringPool pool;
+TEST_F(StringPoolTest, ConstIterator) {
   static char kString[] = "Test String";
-  pool.InternString(kString);
+  pool_.InternString(kString);
 
-  const StringPool& const_pool = pool;
+  const StringPool& const_pool = pool_;
 
   auto it = const_pool.CreateIterator();
   ASSERT_TRUE(it);
@@ -84,9 +88,10 @@
   ASSERT_FALSE(++it);
 }
 
-TEST(StringPoolTest, StressTest) {
-  // First create a buffer with 8MB of random characters.
-  constexpr size_t kBufferSize = 8 * 1024 * 1024;
+TEST_F(StringPoolTest, StressTest) {
+  // First create a buffer with 33MB of random characters, so that we insert
+  // into at least two chunks.
+  constexpr size_t kBufferSize = 33 * 1024 * 1024;
   std::minstd_rand0 rnd_engine(0);
   std::unique_ptr<char[]> buffer(new char[kBufferSize]);
   for (size_t i = 0; i < kBufferSize; i++)
@@ -94,7 +99,6 @@
 
   // Next create strings of length 0 to 16k in length from this buffer and
   // intern them, storing their ids.
-  StringPool pool;
   std::multimap<StringPool::Id, base::StringView> string_map;
   constexpr uint16_t kMaxStrSize = 16u * 1024u - 1;
   for (size_t i = 0;;) {
@@ -103,44 +107,94 @@
       break;
 
     auto str = base::StringView(&buffer.get()[i], length);
-    string_map.emplace(pool.InternString(str), str);
+    string_map.emplace(pool_.InternString(str), str);
     i += length;
   }
 
   // Finally, iterate through each string in the string pool, check that all ids
   // that match in the multimap are equal, and finish by checking we've removed
   // every item in the multimap.
-  for (auto it = pool.CreateIterator(); it; ++it) {
-    ASSERT_EQ(it.StringView(), pool.Get(it.StringId()));
+  for (auto it = pool_.CreateIterator(); it; ++it) {
+    ASSERT_EQ(it.StringView(), pool_.Get(it.StringId()));
 
     auto it_pair = string_map.equal_range(it.StringId());
     for (auto in_it = it_pair.first; in_it != it_pair.second; ++in_it) {
-      ASSERT_EQ(it.StringView(), in_it->second);
+      ASSERT_EQ(it.StringView(), in_it->second)
+          << it.StringId().raw_id() << ": " << it.StringView().Hash() << " vs "
+          << in_it->second.Hash();
     }
     string_map.erase(it_pair.first, it_pair.second);
   }
   ASSERT_EQ(string_map.size(), 0u);
 }
 
-TEST(StringPoolTest, BigString) {
-  constexpr size_t kBigStringSize = 33 * 1024 * 1024;
-  std::unique_ptr<char[]> str1(new char[kBigStringSize + 1]);
-  std::unique_ptr<char[]> str2(new char[kBigStringSize + 1]);
-  for (size_t i = 0; i < kBigStringSize; i++) {
-    str1.get()[i] = 'A' + static_cast<char>(i % 32);
-    str2.get()[i] = 'A' + static_cast<char>((i + 7) % 32);
+TEST_F(StringPoolTest, BigString) {
+  // Two of these should fit into one block, but the third one should go into
+  // the |large_strings_| list.
+  constexpr size_t kBigStringSize = 15 * 1024 * 1024;
+  // Will fit into block 1 after two kBigStringSize strings.
+  constexpr size_t kSmallStringSize = 16 * 1024;
+  // Will not fit into block 1 anymore after 2*kBigStringSize and
+  // 2*kSmallStringSize, but is smaller than kMinLargeStringSizeBytes, so will
+  // start a new block.
+  constexpr size_t kMediumStringSize = 2 * 1024 * 1024;
+  // Would not fit into a block at all, so ahs to go into |large_strings_|.
+  constexpr size_t kEnormousStringSize = 33 * 1024 * 1024;
+
+  constexpr std::array<size_t, 8> kStringSizes = {
+      kBigStringSize,       // block 1
+      kBigStringSize,       // block 1
+      kBigStringSize,       // large strings
+      kSmallStringSize,     // block 1
+      kSmallStringSize,     // block 1
+      kMediumStringSize,    // block 2
+      kEnormousStringSize,  // large strings
+      kBigStringSize        // block 2
+  };
+
+  static_assert(kBigStringSize * 2 + kSmallStringSize * 2 + kMediumStringSize >
+                    kBlockSizeBytes,
+                "medium string shouldn't fit into block 1 for this test");
+  static_assert(kMediumStringSize < kMinLargeStringSizeBytes,
+                "medium string should cause a new block for this test");
+
+  std::array<std::unique_ptr<char[]>, kStringSizes.size()> big_strings;
+  for (size_t i = 0; i < big_strings.size(); i++) {
+    big_strings[i].reset(new char[kStringSizes[i] + 1]);
+    for (size_t j = 0; j < kStringSizes[i]; j++) {
+      big_strings[i].get()[j] = 'A' + static_cast<char>((j + i) % 26);
+    }
+    big_strings[i].get()[kStringSizes[i]] = '\0';
   }
-  str1.get()[kBigStringSize] = '\0';
-  str2.get()[kBigStringSize] = '\0';
 
-  StringPool pool;
-  StringPool::Id id1 =
-      pool.InternString(base::StringView(str1.get(), kBigStringSize));
-  StringPool::Id id2 =
-      pool.InternString(base::StringView(str2.get(), kBigStringSize));
+  std::array<StringPool::Id, kStringSizes.size()> string_ids;
+  for (size_t i = 0; i < big_strings.size(); i++) {
+    string_ids[i] = pool_.InternString(
+        base::StringView(big_strings[i].get(), kStringSizes[i]));
+    // Interning it a second time should return the original id.
+    ASSERT_EQ(string_ids[i], pool_.InternString(base::StringView(
+                                 big_strings[i].get(), kStringSizes[i])));
+  }
 
-  ASSERT_EQ(str1.get(), pool.Get(id1));
-  ASSERT_EQ(str2.get(), pool.Get(id2));
+  ASSERT_FALSE(string_ids[0].is_large_string());
+  ASSERT_FALSE(string_ids[1].is_large_string());
+  ASSERT_TRUE(string_ids[2].is_large_string());
+  ASSERT_FALSE(string_ids[3].is_large_string());
+  ASSERT_FALSE(string_ids[4].is_large_string());
+  ASSERT_FALSE(string_ids[5].is_large_string());
+  ASSERT_TRUE(string_ids[6].is_large_string());
+  ASSERT_FALSE(string_ids[7].is_large_string());
+
+  ASSERT_EQ(string_ids[0].block_index(), 0u);
+  ASSERT_EQ(string_ids[1].block_index(), 0u);
+  ASSERT_EQ(string_ids[3].block_index(), 0u);
+  ASSERT_EQ(string_ids[4].block_index(), 0u);
+  ASSERT_EQ(string_ids[5].block_index(), 1u);
+  ASSERT_EQ(string_ids[7].block_index(), 1u);
+
+  for (size_t i = 0; i < big_strings.size(); i++) {
+    ASSERT_EQ(big_strings[i].get(), pool_.Get(string_ids[i]));
+  }
 }
 
 }  // namespace
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index be92403..5e15f66 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -22,6 +22,7 @@
     "table.cc",
     "table.h",
     "typed_column.h",
+    "typed_column_internal.h",
   ]
   deps = [
     "../../../gn:default_deps",
@@ -34,9 +35,7 @@
 
 perfetto_unittest_source_set("unittests") {
   testonly = true
-  sources = [
-    "compare_unittest.cc",
-  ]
+  sources = [ "compare_unittest.cc" ]
   deps = [
     ":lib",
     "../../../gn:default_deps",
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index fd9f6b2..d8a88b2 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -239,6 +239,7 @@
         return cmp(sparse_vector<T>().GetNonNull(idx)) >= 0;
       });
       break;
+    case FilterOp::kGlob:
     case FilterOp::kLike:
       rm->Intersect(RowMap());
       break;
@@ -312,10 +313,11 @@
         return v.data() != nullptr && compare::String(v, str_value) >= 0;
       });
       break;
+    case FilterOp::kGlob:
     case FilterOp::kLike:
       // TODO(lalitm): either call through to SQLite or reimplement
       // like ourselves.
-      PERFETTO_DLOG("Ignoring like constraint on string column");
+      PERFETTO_DLOG("Ignoring like/glob constraint on string column");
       break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
@@ -372,6 +374,7 @@
         return compare::Numeric(idx, id_value) >= 0;
       });
       break;
+    case FilterOp::kGlob:
     case FilterOp::kLike:
       rm->Intersect(RowMap());
       break;
@@ -451,8 +454,8 @@
     auto a_val = sv.GetNonNull(a_idx);
     auto b_val = sv.GetNonNull(b_idx);
 
-    int res = compare::Numeric(a_val, b_val);
-    return desc ? res > 0 : res < 0;
+    return desc ? compare::Numeric(a_val, b_val) > 0
+                : compare::Numeric(a_val, b_val) < 0;
   });
 }
 
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index ee12912..7321502 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -41,6 +41,7 @@
   kIsNull,
   kIsNotNull,
   kLike,
+  kGlob,
 };
 
 // Represents a constraint on a column.
@@ -146,6 +147,39 @@
     PERFETTO_FATAL("For GCC");
   }
 
+  // Sets the value of the column at the given |row|.
+  void Set(uint32_t row, SqlValue value) {
+    PERFETTO_CHECK(value.type == type());
+    switch (type_) {
+      case ColumnType::kInt32: {
+        mutable_sparse_vector<int32_t>()->Set(
+            row, static_cast<int32_t>(value.long_value));
+        break;
+      }
+      case ColumnType::kUint32: {
+        mutable_sparse_vector<uint32_t>()->Set(
+            row, static_cast<uint32_t>(value.long_value));
+        break;
+      }
+      case ColumnType::kInt64: {
+        mutable_sparse_vector<int64_t>()->Set(
+            row, static_cast<int64_t>(value.long_value));
+        break;
+      }
+      case ColumnType::kDouble: {
+        mutable_sparse_vector<double>()->Set(row, value.double_value);
+        break;
+      }
+      case ColumnType::kString: {
+        PERFETTO_FATAL(
+            "Setting a generic value on a string column is not implemented");
+      }
+      case ColumnType::kId: {
+        PERFETTO_FATAL("Cannot set value on a id column");
+      }
+    }
+  }
+
   // Sorts |idx| in ascending or descending order (determined by |desc|) based
   // on the contents of this column.
   void StableSort(bool desc, std::vector<uint32_t>* idx) const;
@@ -180,7 +214,7 @@
   // Returns the minimum value in this column. Returns nullopt if this column
   // is empty.
   base::Optional<SqlValue> Min() const {
-    if (row_map().size() == 0)
+    if (row_map().empty())
       return base::nullopt;
 
     if (IsSorted())
@@ -194,7 +228,7 @@
   // Returns the minimum value in this column. Returns nullopt if this column
   // is empty.
   base::Optional<SqlValue> Max() const {
-    if (row_map().size() == 0)
+    if (row_map().empty())
       return base::nullopt;
 
     if (IsSorted())
@@ -275,13 +309,6 @@
   JoinKey join_key() const { return JoinKey{col_idx_in_table_}; }
 
  protected:
-  // Returns the string at the index |idx|.
-  // Should only be called when |type_| == ColumnType::kString.
-  NullTermStringView GetStringPoolStringAtIdx(uint32_t idx) const {
-    PERFETTO_DCHECK(type_ == ColumnType::kString);
-    return string_pool_->Get(sparse_vector<StringPool::Id>().GetNonNull(idx));
-  }
-
   // Returns the backing sparse vector cast to contain data of type T.
   // Should only be called when |type_| == ToColumnType<T>().
   template <typename T>
@@ -298,17 +325,6 @@
     return *static_cast<const SparseVector<T>*>(sparse_vector_);
   }
 
-  // Converts a primitive numeric value to an SqlValue of the correct type.
-  template <typename T>
-  static SqlValue NumericToSqlValue(T value) {
-    if (std::is_same<T, double>::value) {
-      return SqlValue::Double(value);
-    } else if (std::is_convertible<T, int64_t>::value) {
-      return SqlValue::Long(value);
-    }
-    PERFETTO_FATAL("Invalid type");
-  }
-
   const StringPool& string_pool() const { return *string_pool_; }
 
  private:
@@ -451,6 +467,7 @@
         rm->Intersect(RowMap(beg, row_map().size()));
         return true;
       }
+      case FilterOp::kGlob:
       case FilterOp::kNe:
       case FilterOp::kIsNull:
       case FilterOp::kIsNotNull:
@@ -506,6 +523,13 @@
     }
   }
 
+  // Returns the string at the index |idx|.
+  // Should only be called when |type_| == ColumnType::kString.
+  NullTermStringView GetStringPoolStringAtIdx(uint32_t idx) const {
+    PERFETTO_DCHECK(type_ == ColumnType::kString);
+    return string_pool_->Get(sparse_vector<StringPool::Id>().GetNonNull(idx));
+  }
+
   // type_ is used to cast sparse_vector_ to the correct type.
   ColumnType type_ = ColumnType::kInt64;
   void* sparse_vector_ = nullptr;
diff --git a/src/trace_processor/db/compare.h b/src/trace_processor/db/compare.h
index 9f3f9a5..0eb89b1 100644
--- a/src/trace_processor/db/compare.h
+++ b/src/trace_processor/db/compare.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACE_PROCESSOR_DB_COMPARE_H_
 #define SRC_TRACE_PROCESSOR_DB_COMPARE_H_
 
+#include <algorithm>
 #include <stdint.h>
 
 #include "perfetto/ext/base/optional.h"
diff --git a/src/trace_processor/db/typed_column.h b/src/trace_processor/db/typed_column.h
index a7183f6..272d465 100644
--- a/src/trace_processor/db/typed_column.h
+++ b/src/trace_processor/db/typed_column.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -18,11 +18,140 @@
 #define SRC_TRACE_PROCESSOR_DB_TYPED_COLUMN_H_
 
 #include "src/trace_processor/db/column.h"
+#include "src/trace_processor/db/typed_column_internal.h"
 
 namespace perfetto {
 namespace trace_processor {
 
+// TypedColumn<T>
+//
+// Introduction:
+// TypedColumn exists to allow efficient access to the data in a Column without
+// having to go through dynamic type checking. There are two main reasons for
+// this:
+// 1. Performance: dynamic type checking is not free and so if this is used
+//    in a particularily hot codepath, the typechecking can be a significant
+//    overhead.
+// 2. Ergonomics: having to convert back and forth from/to SqlValue causes
+//    signifcant clutter in parts of the code which can already be quite hard
+//    to follow (e.g. trackers like StackProfileTracker which perform cross
+//    checking of various ids).
+//
+// Implementation:
+// TypedColumn is implemented as a memberless subclass of Column. This allows
+// us to reinterpret case from a Column* to a TypedColumn<T> where we know the
+// type T. The methods of TypedColumn are type-specialized methods of Column
+// which allow callers to pass raw types instead of using SqlValue.
+//
+// There are two helper classes (tc_internal::TypeHandler and
+// tc_internal::Serializer) where we specialize behaviour which needs to be
+// different based on T. See their class documentation and below for details
+// on their purpose.
+template <typename T>
+struct TypedColumn : public Column {
+ private:
+  using TH = tc_internal::TypeHandler<T>;
+
+  // The non-optional type of the data in this column.
+  using non_optional_type =
+      typename tc_internal::TypeHandler<T>::non_optional_type;
+
+  // The type of data in this column (including Optional wrapper if the type
+  // should be optional).
+  using get_type = typename tc_internal::TypeHandler<T>::get_type;
+
+  // The type which should be passed to SqlValue functions.
+  using sql_value_type = typename tc_internal::TypeHandler<T>::sql_value_type;
+
+  using Serializer = tc_internal::Serializer<non_optional_type>;
+
+ public:
+  // The type which should be stored in the SparseVector.
+  // Used by the macro code when actually constructing the SparseVectors.
+  using serialized_type = typename Serializer::serialized_type;
+
+  // Returns the data in the column at index |row|.
+  // Function chosen when TH::is_optional == true.
+  template <bool is_optional = TH::is_optional>
+  typename std::enable_if<is_optional, get_type>::type operator[](
+      uint32_t row) const {
+    return Serializer::Deserialize(sparse_vector().Get(row_map().Get(row)));
+  }
+
+  // Function chosen when TH::is_optional == false.
+  template <bool is_optional = TH::is_optional>
+  typename std::enable_if<!is_optional, get_type>::type operator[](
+      uint32_t row) const {
+    return Serializer::Deserialize(
+        sparse_vector().GetNonNull(row_map().Get(row)));
+  }
+
+  // Special function only for string types to allow retrieving the string
+  // directly from the column.
+  template <bool is_string = TH::is_string>
+  typename std::enable_if<is_string, NullTermStringView>::type GetString(
+      uint32_t row) const {
+    return string_pool().Get((*this)[row]);
+  }
+
+  // Sets the data in the column at index |row|.
+  void Set(uint32_t row, non_optional_type v) {
+    auto serialized = Serializer::Serialize(v);
+    mutable_sparse_vector()->Set(row_map().Get(row), serialized);
+  }
+
+  // Inserts the value at the end of the column.
+  void Append(T v) {
+    mutable_sparse_vector()->Append(Serializer::Serialize(v));
+  }
+
+  // Returns the row containing the given value in the Column.
+  base::Optional<uint32_t> IndexOf(sql_value_type v) const {
+    return Column::IndexOf(ToValue(v));
+  }
+
+  std::vector<get_type> ToVectorForTesting() const {
+    std::vector<T> result(row_map().size());
+    for (uint32_t i = 0; i < row_map().size(); ++i)
+      result[i] = (*this)[i];
+    return result;
+  }
+
+  // Helper functions to create constraints for the given value.
+  Constraint eq(sql_value_type v) const { return eq_value(ToValue(v)); }
+  Constraint gt(sql_value_type v) const { return gt_value(ToValue(v)); }
+  Constraint lt(sql_value_type v) const { return lt_value(ToValue(v)); }
+  Constraint ne(sql_value_type v) const { return ne_value(ToValue(v)); }
+  Constraint ge(sql_value_type v) const { return ge_value(ToValue(v)); }
+  Constraint le(sql_value_type v) const { return le_value(ToValue(v)); }
+
+  // Implements equality between two items of type |T|.
+  static constexpr bool Equals(T a, T b) { return TH::Equals(a, b); }
+
+  // Encodes the default flags for a column of the current type.
+  static constexpr uint32_t default_flags() {
+    return TH::is_optional ? Flag::kNoFlag : Flag::kNonNull;
+  }
+
+ private:
+  static SqlValue ToValue(double value) { return SqlValue::Double(value); }
+  static SqlValue ToValue(uint32_t value) { return SqlValue::Long(value); }
+  static SqlValue ToValue(int64_t value) { return SqlValue::Long(value); }
+  static SqlValue ToValue(NullTermStringView value) {
+    return SqlValue::String(value.c_str());
+  }
+
+  const SparseVector<serialized_type>& sparse_vector() const {
+    return Column::sparse_vector<serialized_type>();
+  }
+  SparseVector<serialized_type>* mutable_sparse_vector() {
+    return Column::mutable_sparse_vector<serialized_type>();
+  }
+};
+
 // Represents a column containing ids.
+// TODO(lalitm): think about unifying this with TypedColumn in the
+// future.
 template <typename Id>
 struct IdColumn : public Column {
   Id operator[](uint32_t row) const { return Id(row_map().Get(row)); }
@@ -39,168 +168,6 @@
   Constraint le(uint32_t v) const { return le_value(SqlValue::Long(v)); }
 };
 
-// Represents a column containing data with the given type T.
-//
-// This class exists as a memberless subclass of Column (i.e. sizeof(Column) ==
-// sizeof(TypedColumn<T>)); this is because Columns are type erased but we still
-// want low boilerplate methods to get/set rows in columns where we know the
-// type.
-template <typename T>
-struct TypedColumn : public Column {
-  using StoredType = T;
-
-  // Returns the data in the column at index |row|.
-  T operator[](uint32_t row) const {
-    return sparse_vector<T>().GetNonNull(row_map().Get(row));
-  }
-
-  // Sets the data in the column at index |row|.
-  void Set(uint32_t row, T v) {
-    mutable_sparse_vector<T>()->Set(row_map().Get(row), v);
-  }
-
-  // Inserts the value at the end of the column.
-  void Append(T v) { mutable_sparse_vector<T>()->Append(v); }
-
-  // Returns the row containing the given value in the Column.
-  base::Optional<uint32_t> IndexOf(T v) const {
-    return Column::IndexOf(NumericToSqlValue(v));
-  }
-
-  // Implements equality between two items of type |T|.
-  static bool Equals(T a, T b) {
-    // We need to use equal_to here as it could be T == double and because we
-    // enable all compile time warnings, we will get complaints if we just use
-    // a == b.
-    return std::equal_to<T>()(a, b);
-  }
-
-  // Helper functions to create constraints for the given value.
-  Constraint eq(T v) const { return eq_value(NumericToSqlValue(v)); }
-  Constraint gt(T v) const { return gt_value(NumericToSqlValue(v)); }
-  Constraint lt(T v) const { return lt_value(NumericToSqlValue(v)); }
-  Constraint ne(T v) const { return ne_value(NumericToSqlValue(v)); }
-  Constraint ge(T v) const { return ge_value(NumericToSqlValue(v)); }
-  Constraint le(T v) const { return le_value(NumericToSqlValue(v)); }
-
-  // Encodes the default flags for a column of the current type.
-  static constexpr uint32_t default_flags() { return Flag::kNonNull; }
-};
-
-template <typename T>
-struct TypedColumn<base::Optional<T>> : public Column {
-  using StoredType = T;
-
-  // Returns the data in the column at index |row|.
-  base::Optional<T> operator[](uint32_t row) const {
-    return sparse_vector<T>().Get(row_map().Get(row));
-  }
-
-  // Sets the data in the column at index |row|.
-  void Set(uint32_t row, T v) {
-    mutable_sparse_vector<T>()->Set(row_map().Get(row), v);
-  }
-
-  // Inserts the value at the end of the column.
-  void Append(base::Optional<T> v) { mutable_sparse_vector<T>()->Append(v); }
-
-  // Implements equality between two items of type |T|.
-  static bool Equals(base::Optional<T> a, base::Optional<T> b) {
-    // We need to use equal_to here as it could be T == double and because we
-    // enable all compile time warnings, we will get complaints if we just use
-    // a == b. This is the same reason why we can't also just use equal_to using
-    // a and b directly because the optional implementation of equality uses
-    // == which again causes complaints.
-    return a.has_value() == b.has_value() &&
-           (!a.has_value() || std::equal_to<T>()(*a, *b));
-  }
-
-  // Helper functions to create constraints for the given value.
-  Constraint eq(T v) const { return eq_value(NumericToSqlValue(v)); }
-  Constraint gt(T v) const { return gt_value(NumericToSqlValue(v)); }
-  Constraint lt(T v) const { return lt_value(NumericToSqlValue(v)); }
-  Constraint ne(T v) const { return ne_value(NumericToSqlValue(v)); }
-  Constraint ge(T v) const { return ge_value(NumericToSqlValue(v)); }
-  Constraint le(T v) const { return le_value(NumericToSqlValue(v)); }
-
-  // Encodes the default flags for a column of the current type.
-  static constexpr uint32_t default_flags() { return Flag::kNoFlag; }
-};
-
-template <>
-struct TypedColumn<StringPool::Id> : public Column {
-  using StoredType = StringPool::Id;
-
-  // Returns the data in the column at index |row|.
-  StringPool::Id operator[](uint32_t row) const {
-    return sparse_vector<StringPool::Id>().GetNonNull(row_map().Get(row));
-  }
-
-  // Returns the string in the column by looking up the id at |row| in the
-  // StringPool.
-  NullTermStringView GetString(uint32_t row) const {
-    return GetStringPoolStringAtIdx(row_map().Get(row));
-  }
-
-  // Sets the data in the column at index |row|.
-  void Set(uint32_t row, StringPool::Id v) {
-    mutable_sparse_vector<StringPool::Id>()->Set(row_map().Get(row), v);
-  }
-
-  // Inserts the value at the end of the column.
-  void Append(StringPool::Id v) {
-    mutable_sparse_vector<StringPool::Id>()->Append(v);
-  }
-
-  // Returns the row containing the given value in the Column.
-  base::Optional<uint32_t> IndexOf(StringPool::Id v) const {
-    return Column::IndexOf(SqlValue::String(string_pool().Get(v).c_str()));
-  }
-
-  // Returns the row containing the given value in the Column.
-  base::Optional<uint32_t> IndexOf(NullTermStringView v) const {
-    return Column::IndexOf(SqlValue::String(v.c_str()));
-  }
-
-  // Implements equality between two items of type |T|.
-  static bool Equals(StringPool::Id a, StringPool::Id b) { return a == b; }
-
-  // Helper functions to create constraints for the given value.
-  Constraint eq(const char* v) const { return eq_value(SqlValue::String(v)); }
-  Constraint gt(const char* v) const { return gt_value(SqlValue::String(v)); }
-  Constraint lt(const char* v) const { return lt_value(SqlValue::String(v)); }
-  Constraint ne(const char* v) const { return ne_value(SqlValue::String(v)); }
-  Constraint ge(const char* v) const { return ge_value(SqlValue::String(v)); }
-  Constraint le(const char* v) const { return le_value(SqlValue::String(v)); }
-
-  static constexpr uint32_t default_flags() { return Flag::kNonNull; }
-};
-
-template <>
-struct TypedColumn<base::Optional<StringPool::Id>>
-    : public TypedColumn<StringPool::Id> {
-  // Inserts the value at the end of the column.
-  void Append(base::Optional<StringPool::Id> v) {
-    // Since StringPool::Id == 0 is always treated as null, rewrite
-    // base::nullopt -> 0 to remove an extra check at filter time for
-    // base::nullopt. Instead, that code can assume that the SparseVector
-    // layer always returns a valid id and can handle the nullability at the
-    // stringpool level.
-    // TODO(lalitm): remove this special casing if we migrate all tables over
-    // to macro tables and find that we can remove support for null stringids
-    // in the stringpool.
-    return TypedColumn<StringPool::Id>::Append(v ? *v : StringPool::Id(0u));
-  }
-
-  // Implements equality between two items of type |T|.
-  static bool Equals(base::Optional<StringPool::Id> a,
-                     base::Optional<StringPool::Id> b) {
-    return a == b;
-  }
-
-  static constexpr uint32_t default_flags() { return Flag::kNonNull; }
-};
-
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/src/trace_processor/db/typed_column_internal.h b/src/trace_processor/db/typed_column_internal.h
new file mode 100644
index 0000000..181a3a2
--- /dev/null
+++ b/src/trace_processor/db/typed_column_internal.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2020 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_TRACE_PROCESSOR_DB_TYPED_COLUMN_INTERNAL_H_
+#define SRC_TRACE_PROCESSOR_DB_TYPED_COLUMN_INTERNAL_H_
+
+#include "src/trace_processor/db/column.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace tc_internal {
+
+// Serializer converts between the "public" type used by the rest of trace
+// processor and the type we store in the SparseVector.
+template <typename T, typename Enabled = void>
+struct Serializer {
+  using serialized_type = T;
+
+  static serialized_type Serialize(T value) { return value; }
+  static T Deserialize(serialized_type value) { return value; }
+
+  static base::Optional<serialized_type> Serialize(base::Optional<T> value) {
+    return value;
+  }
+  static base::Optional<T> Deserialize(base::Optional<serialized_type> value) {
+    return value;
+  }
+};
+
+// Specialization of Serializer for StringPool types.
+template <>
+struct Serializer<StringPool::Id> {
+  using serialized_type = StringPool::Id;
+
+  static serialized_type Serialize(StringPool::Id value) { return value; }
+  static StringPool::Id Deserialize(serialized_type value) { return value; }
+
+  static serialized_type Serialize(base::Optional<StringPool::Id> value) {
+    // Since StringPool::Id == 0 is always treated as null, rewrite
+    // base::nullopt -> 0 to remove an extra check at filter time for
+    // base::nullopt. Instead, that code can assume that the SparseVector
+    // layer always returns a valid id and can handle the nullability at the
+    // stringpool level.
+    // TODO(lalitm): remove this special casing if we migrate all tables over
+    // to macro tables and find that we can remove support for null stringids
+    // in the stringpool.
+    return value ? Serialize(*value) : StringPool::Id::Null();
+  }
+  static base::Optional<serialized_type> Deserialize(
+      base::Optional<StringPool::Id>) {
+    PERFETTO_FATAL("Should never be storing optional StringPool ids");
+  }
+};
+
+// TypeHandler (and it's specializations) allow for specialied handling of
+// functions of a TypedColumn based on what is being stored inside.
+// Default implementation of TypeHandler.
+template <typename T, typename Enable = void>
+struct TypeHandler {
+  using non_optional_type = T;
+  using get_type = T;
+  using sql_value_type = T;
+
+  static constexpr bool is_optional = false;
+  static constexpr bool is_string = false;
+
+  static bool Equals(T a, T b) {
+    // We need to use equal_to here as it could be T == double and because we
+    // enable all compile time warnings, we will get complaints if we just use
+    // a == b.
+    return std::equal_to<T>()(a, b);
+  }
+};
+
+// Specialization for Optional types.
+template <typename T>
+struct TypeHandler<base::Optional<T>> {
+  using non_optional_type = T;
+  using get_type = base::Optional<T>;
+  using sql_value_type = T;
+
+  static constexpr bool is_optional = true;
+  static constexpr bool is_string = false;
+
+  static bool Equals(base::Optional<T> a, base::Optional<T> b) {
+    // We need to use equal_to here as it could be T == double and because we
+    // enable all compile time warnings, we will get complaints if we just use
+    // a == b. This is the same reason why we can't also just use equal_to using
+    // a and b directly because the optional implementation of equality uses
+    // == which again causes complaints.
+    return a.has_value() == b.has_value() &&
+           (!a.has_value() || std::equal_to<T>()(*a, *b));
+  }
+};
+
+// Specialization for Optional<StringId> types.
+template <>
+struct TypeHandler<StringPool::Id> {
+  // get_type removes the base::Optional since we convert base::nullopt ->
+  // StringPool::Id::Null (see Serializer<StringPool> above).
+  using non_optional_type = StringPool::Id;
+  using get_type = StringPool::Id;
+  using sql_value_type = NullTermStringView;
+
+  static constexpr bool is_optional = false;
+  static constexpr bool is_string = true;
+
+  static bool Equals(StringPool::Id a, StringPool::Id b) { return a == b; }
+};
+
+// Specialization for Optional<StringId> types.
+template <>
+struct TypeHandler<base::Optional<StringPool::Id>> {
+  // get_type removes the base::Optional since we convert base::nullopt ->
+  // StringPool::Id::Null (see Serializer<StringPool> above).
+  using non_optional_type = StringPool::Id;
+  using get_type = StringPool::Id;
+  using sql_value_type = NullTermStringView;
+
+  // is_optional is false again because we always unwrap
+  // base::Optional<StringPool::Id> into StringPool::Id.
+  static constexpr bool is_optional = false;
+  static constexpr bool is_string = true;
+
+  static bool Equals(base::Optional<StringPool::Id> a,
+                     base::Optional<StringPool::Id> b) {
+    // To match our handling of treating base::nullopt ==
+    // StringPool::Id::Null(), ensure that they both compare equal to each
+    // other.
+    return a == b || (!a && b->is_null()) || (!b && a->is_null());
+  }
+};
+
+}  // namespace tc_internal
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DB_TYPED_COLUMN_INTERNAL_H_
diff --git a/src/trace_processor/event_tracker.cc b/src/trace_processor/event_tracker.cc
index 348ac68..cd7c9e4 100644
--- a/src/trace_processor/event_tracker.cc
+++ b/src/trace_processor/event_tracker.cc
@@ -25,7 +25,7 @@
 #include "src/trace_processor/stats.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/track_tracker.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -91,11 +91,12 @@
 }
 
 void EventTracker::FlushPendingEvents() {
+  const auto& thread_table = context_->storage->thread_table();
   for (const auto& pending_counter : pending_upid_resolution_counter_) {
-    const auto& thread = context_->storage->GetThread(pending_counter.utid);
     // TODO(lalitm): having upid == 0 is probably not the correct approach here
     // but it's unclear what may be better.
-    UniquePid upid = thread.upid.value_or(0);
+    UniqueTid utid = pending_counter.utid;
+    UniquePid upid = thread_table.upid()[utid].value_or(0);
     TrackId id = context_->track_tracker->InternProcessCounterTrack(
         pending_counter.name_id, upid);
     context_->storage->mutable_counter_table()->mutable_track_id()->Set(
@@ -103,10 +104,10 @@
   }
 
   for (const auto& pending_instant : pending_upid_resolution_instant_) {
-    const auto& thread = context_->storage->GetThread(pending_instant.utid);
+    UniqueTid utid = pending_instant.utid;
     // TODO(lalitm): having upid == 0 is probably not the correct approach here
     // but it's unclear what may be better.
-    UniquePid upid = thread.upid.value_or(0);
+    UniquePid upid = thread_table.upid()[utid].value_or(0);
     context_->storage->mutable_instant_table()->mutable_ref()->Set(
         pending_instant.row, upid);
   }
diff --git a/src/trace_processor/event_tracker.h b/src/trace_processor/event_tracker.h
index 983dad0..bde0588 100644
--- a/src/trace_processor/event_tracker.h
+++ b/src/trace_processor/event_tracker.h
@@ -76,7 +76,7 @@
   // Represents a counter event which is currently pending upid resolution.
   struct PendingUpidResolutionCounter {
     uint32_t row = 0;
-    StringId name_id = 0;
+    StringId name_id = kNullStringId;
     UniqueTid utid = 0;
   };
 
diff --git a/src/trace_processor/event_tracker_unittest.cc b/src/trace_processor/event_tracker_unittest.cc
index 4fadf41..d8f41ed 100644
--- a/src/trace_processor/event_tracker_unittest.cc
+++ b/src/trace_processor/event_tracker_unittest.cc
@@ -35,6 +35,7 @@
  public:
   EventTrackerTest() {
     context.storage.reset(new TraceStorage());
+    context.global_args_tracker.reset(new GlobalArgsTracker(&context));
     context.args_tracker.reset(new ArgsTracker(&context));
     context.process_tracker.reset(new ProcessTracker(&context));
     context.event_tracker.reset(new EventTracker(&context));
@@ -67,10 +68,11 @@
 
   ASSERT_EQ(timestamps.size(), 2ul);
   ASSERT_EQ(timestamps[0], timestamp);
-  ASSERT_EQ(context.storage->GetThread(1).start_ns, 0);
-  ASSERT_STREQ(
-      context.storage->GetString(context.storage->GetThread(1).name_id).c_str(),
-      kCommProc1);
+  ASSERT_EQ(context.storage->thread_table().start_ts()[1], base::nullopt);
+
+  auto name =
+      context.storage->GetString(context.storage->thread_table().name()[1]);
+  ASSERT_STREQ(name.c_str(), kCommProc1);
   ASSERT_EQ(context.storage->slices().utids().front(), 1u);
   ASSERT_EQ(context.storage->slices().durations().front(), 1);
 }
@@ -101,7 +103,7 @@
 
   ASSERT_EQ(timestamps.size(), 4ul);
   ASSERT_EQ(timestamps[0], timestamp);
-  ASSERT_EQ(context.storage->GetThread(1).start_ns, 0);
+  ASSERT_EQ(context.storage->thread_table().start_ts()[1], base::nullopt);
   ASSERT_EQ(context.storage->slices().durations().at(0), 1u);
   ASSERT_EQ(context.storage->slices().durations().at(1), 11u - 1u);
   ASSERT_EQ(context.storage->slices().durations().at(2), 31u - 11u);
@@ -112,7 +114,7 @@
 TEST_F(EventTrackerTest, CounterDuration) {
   uint32_t cpu = 3;
   int64_t timestamp = 100;
-  StringId name_id = 0;
+  StringId name_id = kNullStringId;
 
   TrackId track = context.track_tracker->InternCpuCounterTrack(name_id, cpu);
   context.event_tracker->PushCounter(timestamp, 1000, track);
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 4aa62b9..42370f7 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -27,11 +27,13 @@
 #include <json/writer.h>
 #include <stdio.h>
 
+#include <algorithm>
 #include <cstring>
 #include <deque>
 #include <limits>
 
 #include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/metadata.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/trace_processor_storage_impl.h"
@@ -46,7 +48,7 @@
 using IndexMap = perfetto::trace_processor::TraceStorage::Stats::IndexMap;
 
 const char kLegacyEventArgsKey[] = "legacy_event";
-const char kLegacyEventOriginalTidKey[] = "original_tid";
+const char kLegacyEventPassthroughUtidKey[] = "passthrough_utid";
 const char kLegacyEventCategoryKey[] = "category";
 const char kLegacyEventNameKey[] = "name";
 const char kLegacyEventPhaseKey[] = "phase";
@@ -72,388 +74,12 @@
   return id == kNullStringId ? "" : storage->GetString(id).c_str();
 }
 
-class FileWriter : public OutputWriter {
- public:
-  FileWriter(FILE* file) : file_(file) {}
-  ~FileWriter() override { fflush(file_); }
-
-  util::Status AppendString(const std::string& s) override {
-    size_t written =
-        fwrite(s.data(), sizeof(std::string::value_type), s.size(), file_);
-    if (written != s.size())
-      return util::ErrStatus("Error writing to file: %d", ferror(file_));
-    return util::OkStatus();
-  }
-
- private:
-  FILE* file_;
-};
-
-class TraceFormatWriter {
- public:
-  TraceFormatWriter(OutputWriter* output,
-                    ArgumentFilterPredicate argument_filter,
-                    MetadataFilterPredicate metadata_filter,
-                    LabelFilterPredicate label_filter)
-      : output_(output),
-        argument_filter_(argument_filter),
-        metadata_filter_(metadata_filter),
-        label_filter_(label_filter),
-        first_event_(true) {
-    WriteHeader();
-  }
-
-  ~TraceFormatWriter() { WriteFooter(); }
-
-  void WriteCommonEvent(const Json::Value& event) {
-    if (label_filter_ && !label_filter_("traceEvents"))
-      return;
-
-    // Pop end events with smaller or equal timestamps.
-    PopEndEvents(event["ts"].asInt64());
-
-    DoWriteEvent(event);
-  }
-
-  void PushEndEvent(const Json::Value& event) {
-    if (label_filter_ && !label_filter_("traceEvents"))
-      return;
-
-    // Pop any end events that end before the new one.
-    PopEndEvents(event["ts"].asInt64() - 1);
-
-    // Catapult doesn't handle out-of-order begin/end events well, especially
-    // when their timestamps are the same, but their order is incorrect. Since
-    // our events are sorted by begin timestamp, we only have to reorder end
-    // events. We do this by buffering them into a stack, so that both begin &
-    // end events of potential child events have been emitted before we emit the
-    // end of a parent event.
-    end_events_.push_back(event);
-  }
-
-  void WriteMetadataEvent(const char* metadata_type,
-                          const char* metadata_value,
-                          uint32_t tid,
-                          uint32_t pid) {
-    if (label_filter_ && !label_filter_("traceEvents"))
-      return;
-
-    if (!first_event_)
-      output_->AppendString(",\n");
-
-    Json::FastWriter writer;
-    writer.omitEndingLineFeed();
-    Json::Value value;
-    value["ph"] = "M";
-    value["cat"] = "__metadata";
-    value["ts"] = 0;
-    value["name"] = metadata_type;
-    value["tid"] = static_cast<int32_t>(tid);
-    value["pid"] = static_cast<int32_t>(pid);
-
-    Json::Value args;
-    args["name"] = metadata_value;
-    value["args"] = args;
-
-    output_->AppendString(writer.write(value));
-    first_event_ = false;
-  }
-
-  void MergeMetadata(const Json::Value& value) {
-    for (const auto& member : value.getMemberNames()) {
-      metadata_[member] = value[member];
-    }
-  }
-
-  void AppendTelemetryMetadataString(const char* key, const char* value) {
-    metadata_["telemetry"][key].append(value);
-  }
-
-  void AppendTelemetryMetadataInt(const char* key, int64_t value) {
-    metadata_["telemetry"][key].append(Json::Int64(value));
-  }
-
-  void AppendTelemetryMetadataBool(const char* key, bool value) {
-    metadata_["telemetry"][key].append(value);
-  }
-
-  void SetTelemetryMetadataTimestamp(const char* key, int64_t value) {
-    metadata_["telemetry"][key] = value / 1000.0;
-  }
-
-  void SetStats(const char* key, int64_t value) {
-    metadata_["trace_processor_stats"][key] = Json::Int64(value);
-  }
-
-  void SetStats(const char* key, const IndexMap& indexed_values) {
-    constexpr const char* kBufferStatsPrefix = "traced_buf_";
-
-    // Stats for the same buffer should be grouped together in the JSON.
-    if (strncmp(kBufferStatsPrefix, key, strlen(kBufferStatsPrefix)) == 0) {
-      for (const auto& value : indexed_values) {
-        metadata_["trace_processor_stats"]["traced_buf"][value.first]
-                 [key + strlen(kBufferStatsPrefix)] = Json::Int64(value.second);
-      }
-      return;
-    }
-
-    // Other indexed value stats are exported as array under their key.
-    for (const auto& value : indexed_values) {
-      metadata_["trace_processor_stats"][key][value.first] =
-          Json::Int64(value.second);
-    }
-  }
-
-  void AddSystemTraceData(const std::string& data) {
-    system_trace_data_ += data;
-  }
-
-  void AddUserTraceData(const std::string& data) {
-    if (user_trace_data_.empty())
-      user_trace_data_ = "[";
-    user_trace_data_ += data;
-  }
-
- private:
-  void WriteHeader() {
-    if (!label_filter_)
-      output_->AppendString("{\"traceEvents\":[\n");
-  }
-
-  void WriteFooter() {
-    PopEndEvents(std::numeric_limits<int64_t>::max());
-
-    // Filter metadata entries.
-    if (metadata_filter_) {
-      for (const auto& member : metadata_.getMemberNames()) {
-        if (!metadata_filter_(member.c_str()))
-          metadata_[member] = kStrippedArgument;
-      }
-    }
-
-    Json::FastWriter writer;
-    writer.omitEndingLineFeed();
-    if ((!label_filter_ || label_filter_("traceEvents")) &&
-        !user_trace_data_.empty()) {
-      user_trace_data_ += "]";
-      Json::Reader reader;
-      Json::Value result;
-      if (reader.parse(user_trace_data_, result)) {
-        for (const auto& event : result) {
-          WriteCommonEvent(event);
-        }
-      } else {
-        PERFETTO_DLOG(
-            "can't parse legacy user json trace export, skipping. data: %s",
-            user_trace_data_.c_str());
-      }
-    }
-    if (!label_filter_)
-      output_->AppendString("]");
-    if ((!label_filter_ || label_filter_("systemTraceEvents")) &&
-        !system_trace_data_.empty()) {
-      output_->AppendString(",\"systemTraceEvents\":\n");
-      output_->AppendString(writer.write(Json::Value(system_trace_data_)));
-    }
-    if ((!label_filter_ || label_filter_("metadata")) && !metadata_.empty()) {
-      output_->AppendString(",\"metadata\":\n");
-      output_->AppendString(writer.write(metadata_));
-    }
-    if (!label_filter_)
-      output_->AppendString("}");
-  }
-
-  void DoWriteEvent(const Json::Value& event) {
-    if (!first_event_)
-      output_->AppendString(",\n");
-
-    Json::FastWriter writer;
-    writer.omitEndingLineFeed();
-
-    ArgumentNameFilterPredicate argument_name_filter;
-    bool strip_args =
-        argument_filter_ &&
-        !argument_filter_(event["cat"].asCString(), event["name"].asCString(),
-                          &argument_name_filter);
-    if ((strip_args || argument_name_filter) && event.isMember("args")) {
-      Json::Value event_copy = event;
-      if (strip_args) {
-        event_copy["args"] = kStrippedArgument;
-      } else {
-        auto& args = event_copy["args"];
-        for (const auto& member : event["args"].getMemberNames()) {
-          if (!argument_name_filter(member.c_str()))
-            args[member] = kStrippedArgument;
-        }
-      }
-      output_->AppendString(writer.write(event_copy));
-    } else {
-      output_->AppendString(writer.write(event));
-    }
-    first_event_ = false;
-  }
-
-  void PopEndEvents(int64_t max_ts) {
-    while (!end_events_.empty()) {
-      int64_t ts = end_events_.back()["ts"].asInt64();
-      if (ts > max_ts)
-        break;
-      DoWriteEvent(end_events_.back());
-      end_events_.pop_back();
-    }
-  }
-
-  OutputWriter* output_;
-  ArgumentFilterPredicate argument_filter_;
-  MetadataFilterPredicate metadata_filter_;
-  LabelFilterPredicate label_filter_;
-
-  bool first_event_;
-  Json::Value metadata_;
-  std::string system_trace_data_;
-  std::string user_trace_data_;
-  std::deque<Json::Value> end_events_;
-};
-
 std::string PrintUint64(uint64_t x) {
   char hex_str[19];
   sprintf(hex_str, "0x%" PRIx64, x);
   return hex_str;
 }
 
-class ArgsBuilder {
- public:
-  explicit ArgsBuilder(const TraceStorage* storage)
-      : storage_(storage),
-        empty_value_(Json::objectValue),
-        nan_value_(Json::StaticString("NaN")),
-        inf_value_(Json::StaticString("Infinity")),
-        neg_inf_value_(Json::StaticString("-Infinity")) {
-    const TraceStorage::Args& args = storage->args();
-    if (args.args_count() == 0) {
-      args_sets_.resize(1, empty_value_);
-      return;
-    }
-    args_sets_.resize(args.set_ids().back() + 1, empty_value_);
-    for (size_t i = 0; i < args.args_count(); ++i) {
-      ArgSetId set_id = args.set_ids()[i];
-      const char* key = GetNonNullString(storage_, args.keys()[i]);
-      Variadic value = args.arg_values()[i];
-      AppendArg(set_id, key, VariadicToJson(value));
-    }
-    PostprocessArgs();
-  }
-
-  const Json::Value& GetArgs(ArgSetId set_id) const {
-    // If |set_id| was empty and added to the storage last, it may not be in
-    // args_sets_.
-    if (set_id > args_sets_.size())
-      return empty_value_;
-    return args_sets_[set_id];
-  }
-
- private:
-  Json::Value VariadicToJson(Variadic variadic) {
-    switch (variadic.type) {
-      case Variadic::kInt:
-        return Json::Int64(variadic.int_value);
-      case Variadic::kUint:
-        return Json::UInt64(variadic.uint_value);
-      case Variadic::kString:
-        return GetNonNullString(storage_, variadic.string_value);
-      case Variadic::kReal:
-        if (std::isnan(variadic.real_value)) {
-          return nan_value_;
-        } else if (std::isinf(variadic.real_value) && variadic.real_value > 0) {
-          return inf_value_;
-        } else if (std::isinf(variadic.real_value) && variadic.real_value < 0) {
-          return neg_inf_value_;
-        } else {
-          return variadic.real_value;
-        }
-      case Variadic::kPointer:
-        return PrintUint64(variadic.pointer_value);
-      case Variadic::kBool:
-        return variadic.bool_value;
-      case Variadic::kJson:
-        Json::Reader reader;
-        Json::Value result;
-        reader.parse(GetNonNullString(storage_, variadic.json_value), result);
-        return result;
-    }
-    PERFETTO_FATAL("Not reached");  // For gcc.
-  }
-
-  void AppendArg(ArgSetId set_id,
-                 const std::string& key,
-                 const Json::Value& value) {
-    Json::Value* target = &args_sets_[set_id];
-    for (base::StringSplitter parts(key, '.'); parts.Next();) {
-      if (PERFETTO_UNLIKELY(!target->isNull() && !target->isObject())) {
-        PERFETTO_DLOG("Malformed arguments. Can't append %s to %s.",
-                      key.c_str(), args_sets_[set_id].toStyledString().c_str());
-        return;
-      }
-      std::string key_part = parts.cur_token();
-      size_t bracketpos = key_part.find('[');
-      if (bracketpos == key_part.npos) {  // A single item
-        target = &(*target)[key_part];
-      } else {  // A list item
-        target = &(*target)[key_part.substr(0, bracketpos)];
-        while (bracketpos != key_part.npos) {
-          std::string index = key_part.substr(
-              bracketpos + 1, key_part.find(']', bracketpos) - bracketpos - 1);
-          if (PERFETTO_UNLIKELY(!target->isNull() && !target->isArray())) {
-            PERFETTO_DLOG("Malformed arguments. Can't append %s to %s.",
-                          key.c_str(),
-                          args_sets_[set_id].toStyledString().c_str());
-            return;
-          }
-          target = &(*target)[stoi(index)];
-          bracketpos = key_part.find('[', bracketpos + 1);
-        }
-      }
-    }
-    *target = value;
-  }
-
-  void PostprocessArgs() {
-    for (Json::Value& args : args_sets_) {
-      // Move all fields from "debug" key to upper level.
-      if (args.isMember("debug")) {
-        Json::Value debug = args["debug"];
-        args.removeMember("debug");
-        for (const auto& member : debug.getMemberNames()) {
-          args[member] = debug[member];
-        }
-      }
-
-      // Rename source fields.
-      if (args.isMember("task")) {
-        if (args["task"].isMember("posted_from")) {
-          Json::Value posted_from = args["task"]["posted_from"];
-          args["task"].removeMember("posted_from");
-          if (posted_from.isMember("function_name")) {
-            args["src_func"] = posted_from["function_name"];
-            args["src_file"] = posted_from["file_name"];
-          } else if (posted_from.isMember("file_name")) {
-            args["src"] = posted_from["file_name"];
-          }
-        }
-        if (args["task"].empty())
-          args.removeMember("task");
-      }
-    }
-  }
-
-  const TraceStorage* storage_;
-  std::vector<Json::Value> args_sets_;
-  const Json::Value empty_value_;
-  const Json::Value nan_value_;
-  const Json::Value inf_value_;
-  const Json::Value neg_inf_value_;
-};
-
 void ConvertLegacyFlowEventArgs(const Json::Value& legacy_args,
                                 Json::Value* event) {
   if (legacy_args.isMember(kLegacyEventBindIdKey)) {
@@ -478,564 +104,1223 @@
   }
 }
 
-util::Status ExportThreadNames(const TraceStorage* storage,
-                               TraceFormatWriter* writer) {
-  for (UniqueTid i = 1; i < storage->thread_count(); ++i) {
-    auto thread = storage->GetThread(i);
-    if (!thread.name_id.is_null()) {
-      const char* thread_name = GetNonNullString(storage, thread.name_id);
-      uint32_t pid = thread.upid ? storage->GetProcess(*thread.upid).pid : 0;
-      writer->WriteMetadataEvent("thread_name", thread_name, thread.tid, pid);
-    }
+class FileWriter : public OutputWriter {
+ public:
+  FileWriter(FILE* file) : file_(file) {}
+  ~FileWriter() override { fflush(file_); }
+
+  util::Status AppendString(const std::string& s) override {
+    size_t written =
+        fwrite(s.data(), sizeof(std::string::value_type), s.size(), file_);
+    if (written != s.size())
+      return util::ErrStatus("Error writing to file: %d", ferror(file_));
+    return util::OkStatus();
   }
-  return util::OkStatus();
-}
 
-util::Status ExportProcessNames(const TraceStorage* storage,
-                                TraceFormatWriter* writer) {
-  for (UniquePid i = 1; i < storage->process_count(); ++i) {
-    auto process = storage->GetProcess(i);
-    if (!process.name_id.is_null()) {
-      const char* process_name = GetNonNullString(storage, process.name_id);
-      writer->WriteMetadataEvent("process_name", process_name, 0, process.pid);
-    }
+ private:
+  FILE* file_;
+};
+
+class JsonExporter {
+ public:
+  JsonExporter(const TraceStorage* storage,
+               OutputWriter* output,
+               ArgumentFilterPredicate argument_filter,
+               MetadataFilterPredicate metadata_filter,
+               LabelFilterPredicate label_filter)
+      : storage_(storage),
+        args_builder_(storage_),
+        writer_(output, argument_filter, metadata_filter, label_filter) {}
+
+  util::Status Export() {
+    util::Status status = MapUniquePidsAndTids();
+    if (!status.ok())
+      return status;
+
+    status = ExportThreadNames();
+    if (!status.ok())
+      return status;
+
+    status = ExportProcessNames();
+    if (!status.ok())
+      return status;
+
+    status = ExportSlices();
+    if (!status.ok())
+      return status;
+
+    status = ExportRawEvents();
+    if (!status.ok())
+      return status;
+
+    status = ExportCpuProfileSamples();
+    if (!status.ok())
+      return status;
+
+    status = ExportMetadata();
+    if (!status.ok())
+      return status;
+
+    status = ExportStats();
+    if (!status.ok())
+      return status;
+
+    return util::OkStatus();
   }
-  return util::OkStatus();
-}
 
-util::Status ExportSlices(const TraceStorage* storage,
-                          const ArgsBuilder& args_builder,
-                          TraceFormatWriter* writer) {
-  const auto& slices = storage->slice_table();
-  for (uint32_t i = 0; i < slices.row_count(); ++i) {
-    Json::Value event;
-    event["ts"] = Json::Int64(slices.ts()[i] / 1000);
-    event["cat"] = GetNonNullString(storage, slices.category()[i]);
-    event["name"] = GetNonNullString(storage, slices.name()[i]);
-    event["pid"] = 0;
-    event["tid"] = 0;
+ private:
+  class TraceFormatWriter {
+   public:
+    TraceFormatWriter(OutputWriter* output,
+                      ArgumentFilterPredicate argument_filter,
+                      MetadataFilterPredicate metadata_filter,
+                      LabelFilterPredicate label_filter)
+        : output_(output),
+          argument_filter_(argument_filter),
+          metadata_filter_(metadata_filter),
+          label_filter_(label_filter),
+          first_event_(true) {
+      WriteHeader();
+    }
 
-    int32_t legacy_tid = 0;
+    ~TraceFormatWriter() { WriteFooter(); }
 
-    event["args"] =
-        args_builder.GetArgs(slices.arg_set_id()[i]);  // Makes a copy.
-    if (event["args"].isMember(kLegacyEventArgsKey)) {
-      ConvertLegacyFlowEventArgs(event["args"][kLegacyEventArgsKey], &event);
+    void WriteCommonEvent(const Json::Value& event) {
+      if (label_filter_ && !label_filter_("traceEvents"))
+        return;
 
-      if (event["args"][kLegacyEventArgsKey].isMember(
-              kLegacyEventOriginalTidKey)) {
-        legacy_tid = static_cast<int32_t>(
-            event["args"][kLegacyEventArgsKey][kLegacyEventOriginalTidKey]
-                .asInt());
+      DoWriteEvent(event);
+    }
+
+    void AddAsyncBeginEvent(const Json::Value& event) {
+      if (label_filter_ && !label_filter_("traceEvents"))
+        return;
+
+      async_begin_events_.push_back(event);
+    }
+
+    void AddAsyncInstantEvent(const Json::Value& event) {
+      if (label_filter_ && !label_filter_("traceEvents"))
+        return;
+
+      async_instant_events_.push_back(event);
+    }
+
+    void AddAsyncEndEvent(const Json::Value& event) {
+      if (label_filter_ && !label_filter_("traceEvents"))
+        return;
+
+      async_end_events_.push_back(event);
+    }
+
+    void SortAndEmitAsyncEvents() {
+      // Catapult doesn't handle out-of-order begin/end events well, especially
+      // when their timestamps are the same, but their order is incorrect. Since
+      // we process events sorted by begin timestamp, |async_begin_events_| and
+      // |async_instant_events_| are already sorted. We now only have to sort
+      // |async_end_events_| and merge-sort all events into a single sequence.
+
+      // Sort |async_end_events_|. Note that we should order by ascending
+      // timestamp, but in reverse-stable order. This way, a child slices's end
+      // is emitted before its parent's end event, even if both end events have
+      // the same timestamp. To accomplish this, we perform a stable sort in
+      // descending order and later iterate via reverse iterators.
+      struct {
+        bool operator()(const Json::Value& a, const Json::Value& b) const {
+          return a["ts"].asInt64() > b["ts"].asInt64();
+        }
+      } CompareEvents;
+      std::stable_sort(async_end_events_.begin(), async_end_events_.end(),
+                       CompareEvents);
+
+      // Merge sort by timestamp. If events share the same timestamp, prefer
+      // instant events, then end events, so that old slices close before new
+      // ones are opened, but instant events remain in their deepest nesting
+      // level.
+      auto instant_event_it = async_instant_events_.begin();
+      auto end_event_it = async_end_events_.rbegin();
+      auto begin_event_it = async_begin_events_.begin();
+
+      auto has_instant_event = instant_event_it != async_instant_events_.end();
+      auto has_end_event = end_event_it != async_end_events_.rend();
+      auto has_begin_event = begin_event_it != async_begin_events_.end();
+
+      auto emit_next_instant = [&instant_event_it, &has_instant_event, this]() {
+        DoWriteEvent(*instant_event_it);
+        instant_event_it++;
+        has_instant_event = instant_event_it != async_instant_events_.end();
+      };
+      auto emit_next_end = [&end_event_it, &has_end_event, this]() {
+        DoWriteEvent(*end_event_it);
+        end_event_it++;
+        has_end_event = end_event_it != async_end_events_.rend();
+      };
+      auto emit_next_begin = [&begin_event_it, &has_begin_event, this]() {
+        DoWriteEvent(*begin_event_it);
+        begin_event_it++;
+        has_begin_event = begin_event_it != async_begin_events_.end();
+      };
+
+      auto emit_next_instant_or_end = [&instant_event_it, &end_event_it,
+                                       &emit_next_instant, &emit_next_end]() {
+        if ((*instant_event_it)["ts"].asInt64() <=
+            (*end_event_it)["ts"].asInt64()) {
+          emit_next_instant();
+        } else {
+          emit_next_end();
+        }
+      };
+      auto emit_next_instant_or_begin = [&instant_event_it, &begin_event_it,
+                                         &emit_next_instant,
+                                         &emit_next_begin]() {
+        if ((*instant_event_it)["ts"].asInt64() <=
+            (*begin_event_it)["ts"].asInt64()) {
+          emit_next_instant();
+        } else {
+          emit_next_begin();
+        }
+      };
+      auto emit_next_end_or_begin = [&end_event_it, &begin_event_it,
+                                     &emit_next_end, &emit_next_begin]() {
+        if ((*end_event_it)["ts"].asInt64() <=
+            (*begin_event_it)["ts"].asInt64()) {
+          emit_next_end();
+        } else {
+          emit_next_begin();
+        }
+      };
+
+      // While we still have events in all iterators, consider each.
+      while (has_instant_event && has_end_event && has_begin_event) {
+        if ((*instant_event_it)["ts"].asInt64() <=
+            (*end_event_it)["ts"].asInt64()) {
+          emit_next_instant_or_begin();
+        } else {
+          emit_next_end_or_begin();
+        }
       }
 
-      event["args"].removeMember(kLegacyEventArgsKey);
+      // Only instant and end events left.
+      while (has_instant_event && has_end_event) {
+        emit_next_instant_or_end();
+      }
+
+      // Only instant and begin events left.
+      while (has_instant_event && has_begin_event) {
+        emit_next_instant_or_begin();
+      }
+
+      // Only end and begin events left.
+      while (has_end_event && has_begin_event) {
+        emit_next_end_or_begin();
+      }
+
+      // Remaining instant events.
+      while (has_instant_event) {
+        emit_next_instant();
+      }
+
+      // Remaining end events.
+      while (has_end_event) {
+        emit_next_end();
+      }
+
+      // Remaining begin events.
+      while (has_begin_event) {
+        emit_next_begin();
+      }
     }
 
-    // To prevent duplicate export of slices, only export slices on descriptor
-    // or chrome tracks (i.e. TrackEvent slices). Slices on other tracks may
-    // also be present as raw events and handled by trace_to_text. Only add more
-    // track types here if they are not already covered by trace_to_text.
-    uint32_t track_id = slices.track_id()[i];
+    void WriteMetadataEvent(const char* metadata_type,
+                            const char* metadata_value,
+                            uint32_t pid,
+                            uint32_t tid) {
+      if (label_filter_ && !label_filter_("traceEvents"))
+        return;
 
-    const auto& track_table = storage->track_table();
+      if (!first_event_)
+        output_->AppendString(",\n");
 
-    uint32_t track_row = *track_table.id().IndexOf(TrackId{track_id});
-    auto track_args_id = track_table.source_arg_set_id()[track_row];
-    if (!track_args_id)
-      continue;
-    const auto& track_args = args_builder.GetArgs(*track_args_id);
-    bool legacy_chrome_track = track_args["source"].asString() == "chrome";
-    if (!track_args.isMember("source") ||
-        (!legacy_chrome_track &&
-         track_args["source"].asString() != "descriptor")) {
-      continue;
+      Json::FastWriter writer;
+      writer.omitEndingLineFeed();
+      Json::Value value;
+      value["ph"] = "M";
+      value["cat"] = "__metadata";
+      value["ts"] = 0;
+      value["name"] = metadata_type;
+      value["pid"] = Json::Int(pid);
+      value["tid"] = Json::Int(tid);
+
+      Json::Value args;
+      args["name"] = metadata_value;
+      value["args"] = args;
+
+      output_->AppendString(writer.write(value));
+      first_event_ = false;
     }
 
-    const auto& thread_track = storage->thread_track_table();
-    const auto& process_track = storage->process_track_table();
-    const auto& thread_slices = storage->thread_slices();
-    const auto& virtual_track_slices = storage->virtual_track_slices();
+    void MergeMetadata(const Json::Value& value) {
+      for (const auto& member : value.getMemberNames()) {
+        metadata_[member] = value[member];
+      }
+    }
 
-    int64_t duration_ns = slices.dur()[i];
-    int64_t thread_ts_ns = 0;
-    int64_t thread_duration_ns = 0;
-    int64_t thread_instruction_count = 0;
-    int64_t thread_instruction_delta = 0;
+    void AppendTelemetryMetadataString(const char* key, const char* value) {
+      metadata_["telemetry"][key].append(value);
+    }
 
-    base::Optional<uint32_t> thread_slice_row =
-        thread_slices.FindRowForSliceId(i);
-    if (thread_slice_row) {
-      thread_ts_ns = thread_slices.thread_timestamp_ns()[*thread_slice_row];
-      thread_duration_ns =
-          thread_slices.thread_duration_ns()[*thread_slice_row];
-      thread_instruction_count =
-          thread_slices.thread_instruction_counts()[*thread_slice_row];
-      thread_instruction_delta =
-          thread_slices.thread_instruction_deltas()[*thread_slice_row];
-    } else {
-      base::Optional<uint32_t> vtrack_slice_row =
-          virtual_track_slices.FindRowForSliceId(i);
-      if (vtrack_slice_row) {
-        thread_ts_ns =
-            virtual_track_slices.thread_timestamp_ns()[*vtrack_slice_row];
+    void AppendTelemetryMetadataInt(const char* key, int64_t value) {
+      metadata_["telemetry"][key].append(Json::Int64(value));
+    }
+
+    void AppendTelemetryMetadataBool(const char* key, bool value) {
+      metadata_["telemetry"][key].append(value);
+    }
+
+    void SetTelemetryMetadataTimestamp(const char* key, int64_t value) {
+      metadata_["telemetry"][key] = value / 1000.0;
+    }
+
+    void SetStats(const char* key, int64_t value) {
+      metadata_["trace_processor_stats"][key] = Json::Int64(value);
+    }
+
+    void SetStats(const char* key, const IndexMap& indexed_values) {
+      constexpr const char* kBufferStatsPrefix = "traced_buf_";
+
+      // Stats for the same buffer should be grouped together in the JSON.
+      if (strncmp(kBufferStatsPrefix, key, strlen(kBufferStatsPrefix)) == 0) {
+        for (const auto& value : indexed_values) {
+          metadata_["trace_processor_stats"]["traced_buf"][value.first]
+                   [key + strlen(kBufferStatsPrefix)] =
+                       Json::Int64(value.second);
+        }
+        return;
+      }
+
+      // Other indexed value stats are exported as array under their key.
+      for (const auto& value : indexed_values) {
+        metadata_["trace_processor_stats"][key][value.first] =
+            Json::Int64(value.second);
+      }
+    }
+
+    void AddSystemTraceData(const std::string& data) {
+      system_trace_data_ += data;
+    }
+
+    void AddUserTraceData(const std::string& data) {
+      if (user_trace_data_.empty())
+        user_trace_data_ = "[";
+      user_trace_data_ += data;
+    }
+
+   private:
+    void WriteHeader() {
+      if (!label_filter_)
+        output_->AppendString("{\"traceEvents\":[\n");
+    }
+
+    void WriteFooter() {
+      SortAndEmitAsyncEvents();
+
+      // Filter metadata entries.
+      if (metadata_filter_) {
+        for (const auto& member : metadata_.getMemberNames()) {
+          if (!metadata_filter_(member.c_str()))
+            metadata_[member] = kStrippedArgument;
+        }
+      }
+
+      Json::FastWriter writer;
+      writer.omitEndingLineFeed();
+      if ((!label_filter_ || label_filter_("traceEvents")) &&
+          !user_trace_data_.empty()) {
+        user_trace_data_ += "]";
+        Json::Reader reader;
+        Json::Value result;
+        if (reader.parse(user_trace_data_, result)) {
+          for (const auto& event : result) {
+            WriteCommonEvent(event);
+          }
+        } else {
+          PERFETTO_DLOG(
+              "can't parse legacy user json trace export, skipping. data: %s",
+              user_trace_data_.c_str());
+        }
+      }
+      if (!label_filter_)
+        output_->AppendString("]");
+      if ((!label_filter_ || label_filter_("systemTraceEvents")) &&
+          !system_trace_data_.empty()) {
+        output_->AppendString(",\"systemTraceEvents\":\n");
+        output_->AppendString(writer.write(Json::Value(system_trace_data_)));
+      }
+      if ((!label_filter_ || label_filter_("metadata")) && !metadata_.empty()) {
+        output_->AppendString(",\"metadata\":\n");
+        output_->AppendString(writer.write(metadata_));
+      }
+      if (!label_filter_)
+        output_->AppendString("}");
+    }
+
+    void DoWriteEvent(const Json::Value& event) {
+      if (!first_event_)
+        output_->AppendString(",\n");
+
+      Json::FastWriter writer;
+      writer.omitEndingLineFeed();
+
+      ArgumentNameFilterPredicate argument_name_filter;
+      bool strip_args =
+          argument_filter_ &&
+          !argument_filter_(event["cat"].asCString(), event["name"].asCString(),
+                            &argument_name_filter);
+      if ((strip_args || argument_name_filter) && event.isMember("args")) {
+        Json::Value event_copy = event;
+        if (strip_args) {
+          event_copy["args"] = kStrippedArgument;
+        } else {
+          auto& args = event_copy["args"];
+          for (const auto& member : event["args"].getMemberNames()) {
+            if (!argument_name_filter(member.c_str()))
+              args[member] = kStrippedArgument;
+          }
+        }
+        output_->AppendString(writer.write(event_copy));
+      } else {
+        output_->AppendString(writer.write(event));
+      }
+      first_event_ = false;
+    }
+
+    OutputWriter* output_;
+    ArgumentFilterPredicate argument_filter_;
+    MetadataFilterPredicate metadata_filter_;
+    LabelFilterPredicate label_filter_;
+
+    bool first_event_;
+    Json::Value metadata_;
+    std::string system_trace_data_;
+    std::string user_trace_data_;
+    std::vector<Json::Value> async_begin_events_;
+    std::vector<Json::Value> async_instant_events_;
+    std::vector<Json::Value> async_end_events_;
+  };
+
+  class ArgsBuilder {
+   public:
+    explicit ArgsBuilder(const TraceStorage* storage)
+        : storage_(storage),
+          empty_value_(Json::objectValue),
+          nan_value_(Json::StaticString("NaN")),
+          inf_value_(Json::StaticString("Infinity")),
+          neg_inf_value_(Json::StaticString("-Infinity")) {
+      const auto& arg_table = storage_->arg_table();
+      uint32_t count = arg_table.row_count();
+      if (count == 0) {
+        args_sets_.resize(1, empty_value_);
+        return;
+      }
+      args_sets_.resize(arg_table.arg_set_id()[count - 1] + 1, empty_value_);
+
+      for (uint32_t i = 0; i < count; ++i) {
+        ArgSetId set_id = arg_table.arg_set_id()[i];
+        const char* key = GetNonNullString(storage_, arg_table.key()[i]);
+        Variadic value = storage_->GetArgValue(i);
+        AppendArg(set_id, key, VariadicToJson(value));
+      }
+      PostprocessArgs();
+    }
+
+    const Json::Value& GetArgs(ArgSetId set_id) const {
+      // If |set_id| was empty and added to the storage last, it may not be in
+      // args_sets_.
+      if (set_id > args_sets_.size())
+        return empty_value_;
+      return args_sets_[set_id];
+    }
+
+   private:
+    Json::Value VariadicToJson(Variadic variadic) {
+      switch (variadic.type) {
+        case Variadic::kInt:
+          return Json::Int64(variadic.int_value);
+        case Variadic::kUint:
+          return Json::UInt64(variadic.uint_value);
+        case Variadic::kString:
+          return GetNonNullString(storage_, variadic.string_value);
+        case Variadic::kReal:
+          if (std::isnan(variadic.real_value)) {
+            return nan_value_;
+          } else if (std::isinf(variadic.real_value) &&
+                     variadic.real_value > 0) {
+            return inf_value_;
+          } else if (std::isinf(variadic.real_value) &&
+                     variadic.real_value < 0) {
+            return neg_inf_value_;
+          } else {
+            return variadic.real_value;
+          }
+        case Variadic::kPointer:
+          return PrintUint64(variadic.pointer_value);
+        case Variadic::kBool:
+          return variadic.bool_value;
+        case Variadic::kJson:
+          Json::Reader reader;
+          Json::Value result;
+          reader.parse(GetNonNullString(storage_, variadic.json_value), result);
+          return result;
+      }
+      PERFETTO_FATAL("Not reached");  // For gcc.
+    }
+
+    void AppendArg(ArgSetId set_id,
+                   const std::string& key,
+                   const Json::Value& value) {
+      Json::Value* target = &args_sets_[set_id];
+      for (base::StringSplitter parts(key, '.'); parts.Next();) {
+        if (PERFETTO_UNLIKELY(!target->isNull() && !target->isObject())) {
+          PERFETTO_DLOG("Malformed arguments. Can't append %s to %s.",
+                        key.c_str(),
+                        args_sets_[set_id].toStyledString().c_str());
+          return;
+        }
+        std::string key_part = parts.cur_token();
+        size_t bracketpos = key_part.find('[');
+        if (bracketpos == key_part.npos) {  // A single item
+          target = &(*target)[key_part];
+        } else {  // A list item
+          target = &(*target)[key_part.substr(0, bracketpos)];
+          while (bracketpos != key_part.npos) {
+            // We constructed this string from an int earlier in trace_processor
+            // so it shouldn't be possible for this (or the StringToUInt32
+            // below) to fail.
+            std::string s =
+                key_part.substr(bracketpos + 1, key_part.find(']', bracketpos) -
+                                                    bracketpos - 1);
+            if (PERFETTO_UNLIKELY(!target->isNull() && !target->isArray())) {
+              PERFETTO_DLOG("Malformed arguments. Can't append %s to %s.",
+                            key.c_str(),
+                            args_sets_[set_id].toStyledString().c_str());
+              return;
+            }
+            base::Optional<uint32_t> index = base::StringToUInt32(s);
+            if (PERFETTO_UNLIKELY(!index)) {
+              PERFETTO_ELOG("Expected to be able to extract index from %s",
+                            key_part.c_str());
+              return;
+            }
+            target = &(*target)[index.value()];
+            bracketpos = key_part.find('[', bracketpos + 1);
+          }
+        }
+      }
+      *target = value;
+    }
+
+    void PostprocessArgs() {
+      for (Json::Value& args : args_sets_) {
+        // Move all fields from "debug" key to upper level.
+        if (args.isMember("debug")) {
+          Json::Value debug = args["debug"];
+          args.removeMember("debug");
+          for (const auto& member : debug.getMemberNames()) {
+            args[member] = debug[member];
+          }
+        }
+
+        // Rename source fields.
+        if (args.isMember("task")) {
+          if (args["task"].isMember("posted_from")) {
+            Json::Value posted_from = args["task"]["posted_from"];
+            args["task"].removeMember("posted_from");
+            if (posted_from.isMember("function_name")) {
+              args["src_func"] = posted_from["function_name"];
+              args["src_file"] = posted_from["file_name"];
+            } else if (posted_from.isMember("file_name")) {
+              args["src"] = posted_from["file_name"];
+            }
+          }
+          if (args["task"].empty())
+            args.removeMember("task");
+        }
+      }
+    }
+
+    const TraceStorage* storage_;
+    std::vector<Json::Value> args_sets_;
+    const Json::Value empty_value_;
+    const Json::Value nan_value_;
+    const Json::Value inf_value_;
+    const Json::Value neg_inf_value_;
+  };
+
+  util::Status MapUniquePidsAndTids() {
+    const auto& process_table = storage_->process_table();
+    for (UniquePid upid = 0; upid < process_table.row_count(); upid++) {
+      uint32_t exported_pid = process_table.pid()[upid];
+      auto it_and_inserted =
+          exported_pids_to_upids_.emplace(exported_pid, upid);
+      if (!it_and_inserted.second) {
+        exported_pid = NextExportedPidOrTidForDuplicates();
+        it_and_inserted = exported_pids_to_upids_.emplace(exported_pid, upid);
+      }
+      upids_to_exported_pids_.emplace(upid, exported_pid);
+    }
+
+    const auto& thread_table = storage_->thread_table();
+    for (UniqueTid utid = 0; utid < thread_table.row_count(); utid++) {
+      uint32_t exported_pid = 0;
+      base::Optional<UniquePid> upid = thread_table.upid()[utid];
+      if (upid) {
+        auto exported_pid_it = upids_to_exported_pids_.find(*upid);
+        PERFETTO_DCHECK(exported_pid_it != upids_to_exported_pids_.end());
+        exported_pid = exported_pid_it->second;
+      }
+
+      uint32_t exported_tid = thread_table.tid()[utid];
+      auto it_and_inserted = exported_pids_and_tids_to_utids_.emplace(
+          std::make_pair(exported_pid, exported_tid), utid);
+      if (!it_and_inserted.second) {
+        exported_tid = NextExportedPidOrTidForDuplicates();
+        it_and_inserted = exported_pids_and_tids_to_utids_.emplace(
+            std::make_pair(exported_pid, exported_tid), utid);
+      }
+      utids_to_exported_pids_and_tids_.emplace(
+          utid, std::make_pair(exported_pid, exported_tid));
+    }
+
+    return util::OkStatus();
+  }
+
+  util::Status ExportThreadNames() {
+    const auto& thread_table = storage_->thread_table();
+    for (UniqueTid utid = 0; utid < thread_table.row_count(); ++utid) {
+      auto opt_name = thread_table.name()[utid];
+      if (!opt_name.is_null()) {
+        const char* thread_name = GetNonNullString(storage_, opt_name);
+        auto pid_and_tid = UtidToPidAndTid(utid);
+        writer_.WriteMetadataEvent("thread_name", thread_name,
+                                   pid_and_tid.first, pid_and_tid.second);
+      }
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ExportProcessNames() {
+    const auto& process_table = storage_->process_table();
+    for (UniquePid upid = 0; upid < process_table.row_count(); ++upid) {
+      auto opt_name = process_table.name()[upid];
+      if (!opt_name.is_null()) {
+        const char* process_name = GetNonNullString(storage_, opt_name);
+        writer_.WriteMetadataEvent("process_name", process_name,
+                                   UpidToPid(upid), /*tid=*/0);
+      }
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ExportSlices() {
+    const auto& slices = storage_->slice_table();
+    for (uint32_t i = 0; i < slices.row_count(); ++i) {
+      // Skip slices with empty category - these are ftrace/system slices that
+      // were also imported into the raw table and will be exported from there
+      // by trace_to_text.
+      if (slices.category()[i] == kNullStringId)
+        continue;
+
+      Json::Value event;
+      event["ts"] = Json::Int64(slices.ts()[i] / 1000);
+      event["cat"] = GetNonNullString(storage_, slices.category()[i]);
+      event["name"] = GetNonNullString(storage_, slices.name()[i]);
+      event["pid"] = 0;
+      event["tid"] = 0;
+
+      base::Optional<UniqueTid> legacy_utid;
+
+      event["args"] =
+          args_builder_.GetArgs(slices.arg_set_id()[i]);  // Makes a copy.
+      if (event["args"].isMember(kLegacyEventArgsKey)) {
+        ConvertLegacyFlowEventArgs(event["args"][kLegacyEventArgsKey], &event);
+
+        if (event["args"][kLegacyEventArgsKey].isMember(
+                kLegacyEventPassthroughUtidKey)) {
+          legacy_utid =
+              event["args"][kLegacyEventArgsKey][kLegacyEventPassthroughUtidKey]
+                  .asUInt();
+        }
+
+        event["args"].removeMember(kLegacyEventArgsKey);
+      }
+
+      // To prevent duplicate export of slices, only export slices on descriptor
+      // or chrome tracks (i.e. TrackEvent slices). Slices on other tracks may
+      // also be present as raw events and handled by trace_to_text. Only add
+      // more track types here if they are not already covered by trace_to_text.
+      uint32_t track_id = slices.track_id()[i];
+
+      const auto& track_table = storage_->track_table();
+
+      uint32_t track_row = *track_table.id().IndexOf(TrackId{track_id});
+      auto track_args_id = track_table.source_arg_set_id()[track_row];
+      const Json::Value* track_args = nullptr;
+      bool legacy_chrome_track = false;
+      bool is_child_track = false;
+      if (track_args_id) {
+        track_args = &args_builder_.GetArgs(*track_args_id);
+        legacy_chrome_track = (*track_args)["source"].asString() == "chrome";
+        is_child_track = track_args->isMember("parent_track_id");
+      }
+
+      const auto& thread_track = storage_->thread_track_table();
+      const auto& process_track = storage_->process_track_table();
+      const auto& thread_slices = storage_->thread_slices();
+      const auto& virtual_track_slices = storage_->virtual_track_slices();
+
+      int64_t duration_ns = slices.dur()[i];
+      int64_t thread_ts_ns = 0;
+      int64_t thread_duration_ns = 0;
+      int64_t thread_instruction_count = 0;
+      int64_t thread_instruction_delta = 0;
+
+      base::Optional<uint32_t> thread_slice_row =
+          thread_slices.FindRowForSliceId(i);
+      if (thread_slice_row) {
+        thread_ts_ns = thread_slices.thread_timestamp_ns()[*thread_slice_row];
         thread_duration_ns =
-            virtual_track_slices.thread_duration_ns()[*vtrack_slice_row];
+            thread_slices.thread_duration_ns()[*thread_slice_row];
         thread_instruction_count =
-            virtual_track_slices.thread_instruction_counts()[*vtrack_slice_row];
+            thread_slices.thread_instruction_counts()[*thread_slice_row];
         thread_instruction_delta =
-            virtual_track_slices.thread_instruction_deltas()[*vtrack_slice_row];
-      }
-    }
-
-    auto opt_thread_track_row = thread_track.id().IndexOf(TrackId{track_id});
-
-    if (opt_thread_track_row) {
-      // Synchronous (thread) slice or instant event.
-      UniqueTid utid = thread_track.utid()[*opt_thread_track_row];
-      auto thread = storage->GetThread(utid);
-      event["tid"] = static_cast<int32_t>(thread.tid);
-      if (thread.upid) {
-        event["pid"] =
-            static_cast<int32_t>(storage->GetProcess(*thread.upid).pid);
-      }
-
-      if (duration_ns == 0) {
-        // Use "I" instead of "i" phase for backwards-compat with old consumers.
-        event["ph"] = "I";
-        if (thread_ts_ns > 0) {
-          event["tts"] = Json::Int64(thread_ts_ns / 1000);
-        }
-        if (thread_instruction_count > 0) {
-          event["ticount"] = Json::Int64(thread_instruction_count);
-        }
-        event["s"] = "t";
+            thread_slices.thread_instruction_deltas()[*thread_slice_row];
       } else {
-        if (duration_ns > 0) {
-          event["ph"] = "X";
-          event["dur"] = Json::Int64(duration_ns / 1000);
-        } else {
-          // If the slice didn't finish, the duration may be negative. Only
-          // write a begin event without end event in this case.
-          event["ph"] = "B";
-        }
-        if (thread_ts_ns > 0) {
-          event["tts"] = Json::Int64(thread_ts_ns / 1000);
-          // Only write thread duration for completed events.
-          if (duration_ns > 0)
-            event["tdur"] = Json::Int64(thread_duration_ns / 1000);
-        }
-        if (thread_instruction_count > 0) {
-          event["ticount"] = Json::Int64(thread_instruction_count);
-          // Only write thread instruction delta for completed events.
-          if (duration_ns > 0)
-            event["tidelta"] = Json::Int64(thread_instruction_delta);
-        }
-      }
-      writer->WriteCommonEvent(event);
-    } else if (!legacy_chrome_track ||
-               (legacy_chrome_track && track_args.isMember("source_id"))) {
-      // Async event slice.
-      auto opt_process_row = process_track.id().IndexOf(TrackId{track_id});
-      if (legacy_chrome_track) {
-        // Legacy async tracks are always process-associated.
-        PERFETTO_DCHECK(opt_process_row);
-        uint32_t upid = process_track.upid()[*opt_process_row];
-        event["pid"] = static_cast<int32_t>(storage->GetProcess(upid).pid);
-        event["tid"] =
-            legacy_tid ? legacy_tid
-                       : static_cast<int32_t>(storage->GetProcess(upid).pid);
-
-        // Preserve original event IDs for legacy tracks. This is so that e.g.
-        // memory dump IDs show up correctly in the JSON trace.
-        PERFETTO_DCHECK(track_args.isMember("source_id"));
-        PERFETTO_DCHECK(track_args.isMember("source_id_is_process_scoped"));
-        PERFETTO_DCHECK(track_args.isMember("source_scope"));
-        uint64_t source_id =
-            static_cast<uint64_t>(track_args["source_id"].asInt64());
-        std::string source_scope = track_args["source_scope"].asString();
-        if (!source_scope.empty())
-          event["scope"] = source_scope;
-        bool source_id_is_process_scoped =
-            track_args["source_id_is_process_scoped"].asBool();
-        if (source_id_is_process_scoped) {
-          event["id2"]["local"] = PrintUint64(source_id);
-        } else {
-          // Some legacy importers don't understand "id2" fields, so we use the
-          // "usually" global "id" field instead. This works as long as the
-          // event phase is not in {'N', 'D', 'O', '(', ')'}, see
-          // "LOCAL_ID_PHASES" in catapult.
-          event["id"] = PrintUint64(source_id);
-        }
-      } else {
-        if (opt_process_row) {
-          uint32_t upid = process_track.upid()[*opt_process_row];
-          event["id2"]["local"] = PrintUint64(track_id);
-          event["pid"] = static_cast<int32_t>(storage->GetProcess(upid).pid);
-          event["tid"] =
-              legacy_tid ? legacy_tid
-                         : static_cast<int32_t>(storage->GetProcess(upid).pid);
-        } else {
-          // Some legacy importers don't understand "id2" fields, so we use the
-          // "usually" global "id" field instead. This works as long as the
-          // event phase is not in {'N', 'D', 'O', '(', ')'}, see
-          // "LOCAL_ID_PHASES" in catapult.
-          event["id"] = PrintUint64(track_id);
+        base::Optional<uint32_t> vtrack_slice_row =
+            virtual_track_slices.FindRowForSliceId(i);
+        if (vtrack_slice_row) {
+          thread_ts_ns =
+              virtual_track_slices.thread_timestamp_ns()[*vtrack_slice_row];
+          thread_duration_ns =
+              virtual_track_slices.thread_duration_ns()[*vtrack_slice_row];
+          thread_instruction_count =
+              virtual_track_slices
+                  .thread_instruction_counts()[*vtrack_slice_row];
+          thread_instruction_delta =
+              virtual_track_slices
+                  .thread_instruction_deltas()[*vtrack_slice_row];
         }
       }
 
-      if (thread_ts_ns > 0) {
-        event["tts"] = Json::Int64(thread_ts_ns / 1000);
-        event["use_async_tts"] = Json::Int(1);
-      }
-      if (thread_instruction_count > 0) {
-        event["ticount"] = Json::Int64(thread_instruction_count);
-        event["use_async_tts"] = Json::Int(1);
-      }
+      auto opt_thread_track_row = thread_track.id().IndexOf(TrackId{track_id});
 
-      if (duration_ns == 0) {  // Instant async event.
-        event["ph"] = "n";
-        writer->WriteCommonEvent(event);
-      } else {  // Async start and end.
-        event["ph"] = "b";
-        writer->WriteCommonEvent(event);
-        // If the slice didn't finish, the duration may be negative. Don't
-        // write the end event in this case.
-        if (duration_ns > 0) {
-          event["ph"] = "e";
-          event["ts"] = Json::Int64((slices.ts()[i] + duration_ns) / 1000);
+      if (opt_thread_track_row && !is_child_track) {
+        // Synchronous (thread) slice or instant event.
+        UniqueTid utid = thread_track.utid()[*opt_thread_track_row];
+        auto pid_and_tid = UtidToPidAndTid(utid);
+        event["pid"] = Json::Int(pid_and_tid.first);
+        event["tid"] = Json::Int(pid_and_tid.second);
+
+        if (duration_ns == 0) {
+          // Use "I" instead of "i" phase for backwards-compat with old
+          // consumers.
+          event["ph"] = "I";
           if (thread_ts_ns > 0) {
-            event["tts"] =
-                Json::Int64((thread_ts_ns + thread_duration_ns) / 1000);
+            event["tts"] = Json::Int64(thread_ts_ns / 1000);
           }
           if (thread_instruction_count > 0) {
-            event["ticount"] = Json::Int64(
-                (thread_instruction_count + thread_instruction_delta));
+            event["ticount"] = Json::Int64(thread_instruction_count);
           }
-          event["args"].clear();
-          writer->PushEndEvent(event);
+          event["s"] = "t";
+        } else {
+          if (duration_ns > 0) {
+            event["ph"] = "X";
+            event["dur"] = Json::Int64(duration_ns / 1000);
+          } else {
+            // If the slice didn't finish, the duration may be negative. Only
+            // write a begin event without end event in this case.
+            event["ph"] = "B";
+          }
+          if (thread_ts_ns > 0) {
+            event["tts"] = Json::Int64(thread_ts_ns / 1000);
+            // Only write thread duration for completed events.
+            if (duration_ns > 0)
+              event["tdur"] = Json::Int64(thread_duration_ns / 1000);
+          }
+          if (thread_instruction_count > 0) {
+            event["ticount"] = Json::Int64(thread_instruction_count);
+            // Only write thread instruction delta for completed events.
+            if (duration_ns > 0)
+              event["tidelta"] = Json::Int64(thread_instruction_delta);
+          }
+        }
+        writer_.WriteCommonEvent(event);
+      } else if (is_child_track ||
+                 (legacy_chrome_track && track_args->isMember("source_id"))) {
+        // Async event slice.
+        auto opt_process_row = process_track.id().IndexOf(TrackId{track_id});
+        if (legacy_chrome_track) {
+          // Legacy async tracks are always process-associated and have args.
+          PERFETTO_DCHECK(opt_process_row);
+          PERFETTO_DCHECK(track_args);
+          uint32_t upid = process_track.upid()[*opt_process_row];
+          uint32_t exported_pid = UpidToPid(upid);
+          event["pid"] = Json::Int(exported_pid);
+          event["tid"] =
+              Json::Int(legacy_utid ? UtidToPidAndTid(*legacy_utid).second
+                                    : exported_pid);
+
+          // Preserve original event IDs for legacy tracks. This is so that e.g.
+          // memory dump IDs show up correctly in the JSON trace.
+          PERFETTO_DCHECK(track_args->isMember("source_id"));
+          PERFETTO_DCHECK(track_args->isMember("source_id_is_process_scoped"));
+          PERFETTO_DCHECK(track_args->isMember("source_scope"));
+          uint64_t source_id =
+              static_cast<uint64_t>((*track_args)["source_id"].asInt64());
+          std::string source_scope = (*track_args)["source_scope"].asString();
+          if (!source_scope.empty())
+            event["scope"] = source_scope;
+          bool source_id_is_process_scoped =
+              (*track_args)["source_id_is_process_scoped"].asBool();
+          if (source_id_is_process_scoped) {
+            event["id2"]["local"] = PrintUint64(source_id);
+          } else {
+            // Some legacy importers don't understand "id2" fields, so we use
+            // the "usually" global "id" field instead. This works as long as
+            // the event phase is not in {'N', 'D', 'O', '(', ')'}, see
+            // "LOCAL_ID_PHASES" in catapult.
+            event["id"] = PrintUint64(source_id);
+          }
+        } else {
+          if (opt_thread_track_row) {
+            UniqueTid utid = thread_track.utid()[*opt_thread_track_row];
+            auto pid_and_tid = UtidToPidAndTid(utid);
+            event["pid"] = Json::Int(pid_and_tid.first);
+            event["tid"] = Json::Int(pid_and_tid.second);
+            event["id2"]["local"] = PrintUint64(track_id);
+          } else if (opt_process_row) {
+            uint32_t upid = process_track.upid()[*opt_process_row];
+            uint32_t exported_pid = UpidToPid(upid);
+            event["pid"] = Json::Int(exported_pid);
+            event["tid"] =
+                Json::Int(legacy_utid ? UtidToPidAndTid(*legacy_utid).second
+                                      : exported_pid);
+            event["id2"]["local"] = PrintUint64(track_id);
+          } else {
+            // Some legacy importers don't understand "id2" fields, so we use
+            // the "usually" global "id" field instead. This works as long as
+            // the event phase is not in {'N', 'D', 'O', '(', ')'}, see
+            // "LOCAL_ID_PHASES" in catapult.
+            event["id"] = PrintUint64(track_id);
+          }
+        }
+
+        if (thread_ts_ns > 0) {
+          event["tts"] = Json::Int64(thread_ts_ns / 1000);
+          event["use_async_tts"] = Json::Int(1);
+        }
+        if (thread_instruction_count > 0) {
+          event["ticount"] = Json::Int64(thread_instruction_count);
+          event["use_async_tts"] = Json::Int(1);
+        }
+
+        if (duration_ns == 0) {  // Instant async event.
+          event["ph"] = "n";
+          writer_.AddAsyncInstantEvent(event);
+        } else {  // Async start and end.
+          event["ph"] = "b";
+          writer_.AddAsyncBeginEvent(event);
+          // If the slice didn't finish, the duration may be negative. Don't
+          // write the end event in this case.
+          if (duration_ns > 0) {
+            event["ph"] = "e";
+            event["ts"] = Json::Int64((slices.ts()[i] + duration_ns) / 1000);
+            if (thread_ts_ns > 0) {
+              event["tts"] =
+                  Json::Int64((thread_ts_ns + thread_duration_ns) / 1000);
+            }
+            if (thread_instruction_count > 0) {
+              event["ticount"] = Json::Int64(
+                  (thread_instruction_count + thread_instruction_delta));
+            }
+            event["args"].clear();
+            writer_.AddAsyncEndEvent(event);
+          }
+        }
+      } else {
+        // Global or process-scoped instant event.
+        PERFETTO_DCHECK(legacy_chrome_track || !is_child_track);
+        if (duration_ns != 0) {
+          // We don't support exporting slices on the default global or process
+          // track to JSON (JSON only supports instant events on these tracks).
+          PERFETTO_DLOG(
+              "skipping non-instant slice on global or process track");
+        } else {
+          // Use "I" instead of "i" phase for backwards-compat with old
+          // consumers.
+          event["ph"] = "I";
+
+          auto opt_process_row = process_track.id().IndexOf(TrackId{track_id});
+          if (opt_process_row.has_value()) {
+            uint32_t upid = process_track.upid()[*opt_process_row];
+            uint32_t exported_pid = UpidToPid(upid);
+            event["pid"] = Json::Int(exported_pid);
+            event["tid"] =
+                Json::Int(legacy_utid ? UtidToPidAndTid(*legacy_utid).second
+                                      : exported_pid);
+            event["s"] = "p";
+          } else {
+            event["s"] = "g";
+          }
+          writer_.WriteCommonEvent(event);
         }
       }
-    } else {
-      // Global or process-scoped instant event.
-      PERFETTO_DCHECK(duration_ns == 0);
-      // Use "I" instead of "i" phase for backwards-compat with old consumers.
-      event["ph"] = "I";
-
-      auto opt_process_row = process_track.id().IndexOf(TrackId{track_id});
-      if (opt_process_row.has_value()) {
-        uint32_t upid = process_track.upid()[*opt_process_row];
-        event["pid"] = static_cast<int32_t>(storage->GetProcess(upid).pid);
-        event["tid"] =
-            legacy_tid ? legacy_tid
-                       : static_cast<int32_t>(storage->GetProcess(upid).pid);
-        event["s"] = "p";
-      } else {
-        event["s"] = "g";
-      }
-      writer->WriteCommonEvent(event);
     }
-  }
-  return util::OkStatus();
-}
-
-Json::Value ConvertLegacyRawEventToJson(const TraceStorage* storage,
-                                        const ArgsBuilder& args_builder,
-                                        uint32_t index) {
-  const auto& events = storage->raw_events();
-
-  Json::Value event;
-  event["ts"] = Json::Int64(events.timestamps()[index] / 1000);
-
-  UniqueTid utid = static_cast<UniqueTid>(events.utids()[index]);
-  auto thread = storage->GetThread(utid);
-  event["tid"] = static_cast<int32_t>(thread.tid);
-  event["pid"] = 0;
-  if (thread.upid)
-    event["pid"] = static_cast<int32_t>(storage->GetProcess(*thread.upid).pid);
-
-  // Raw legacy events store all other params in the arg set. Make a copy of
-  // the converted args here, parse, and then remove the legacy params.
-  event["args"] = args_builder.GetArgs(events.arg_set_ids()[index]);
-  const Json::Value& legacy_args = event["args"][kLegacyEventArgsKey];
-
-  PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventCategoryKey));
-  event["cat"] = legacy_args[kLegacyEventCategoryKey];
-
-  PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventNameKey));
-  event["name"] = legacy_args[kLegacyEventNameKey];
-
-  PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventPhaseKey));
-  event["ph"] = legacy_args[kLegacyEventPhaseKey];
-
-  // Object snapshot events are supposed to have a mandatory "snapshot" arg,
-  // which may be removed in trace processor if it is empty.
-  if (legacy_args[kLegacyEventPhaseKey] == "O" &&
-      !event["args"].isMember("snapshot")) {
-    event["args"]["snapshot"] = Json::Value(Json::objectValue);
+    return util::OkStatus();
   }
 
-  if (legacy_args.isMember(kLegacyEventDurationNsKey))
-    event["dur"] = legacy_args[kLegacyEventDurationNsKey].asInt64() / 1000;
+  Json::Value ConvertLegacyRawEventToJson(uint32_t index) {
+    const auto& events = storage_->raw_table();
 
-  if (legacy_args.isMember(kLegacyEventThreadTimestampNsKey)) {
-    event["tts"] =
-        legacy_args[kLegacyEventThreadTimestampNsKey].asInt64() / 1000;
-  }
-
-  if (legacy_args.isMember(kLegacyEventThreadDurationNsKey)) {
-    event["tdur"] =
-        legacy_args[kLegacyEventThreadDurationNsKey].asInt64() / 1000;
-  }
-
-  if (legacy_args.isMember(kLegacyEventThreadInstructionCountKey))
-    event["ticount"] = legacy_args[kLegacyEventThreadInstructionCountKey];
-
-  if (legacy_args.isMember(kLegacyEventThreadInstructionDeltaKey))
-    event["tidelta"] = legacy_args[kLegacyEventThreadInstructionDeltaKey];
-
-  if (legacy_args.isMember(kLegacyEventUseAsyncTtsKey))
-    event["use_async_tts"] = legacy_args[kLegacyEventUseAsyncTtsKey];
-
-  if (legacy_args.isMember(kLegacyEventUnscopedIdKey)) {
-    event["id"] =
-        PrintUint64(legacy_args[kLegacyEventUnscopedIdKey].asUInt64());
-  }
-
-  if (legacy_args.isMember(kLegacyEventGlobalIdKey)) {
-    event["id2"]["global"] =
-        PrintUint64(legacy_args[kLegacyEventGlobalIdKey].asUInt64());
-  }
-
-  if (legacy_args.isMember(kLegacyEventLocalIdKey)) {
-    event["id2"]["local"] =
-        PrintUint64(legacy_args[kLegacyEventLocalIdKey].asUInt64());
-  }
-
-  if (legacy_args.isMember(kLegacyEventIdScopeKey))
-    event["scope"] = legacy_args[kLegacyEventIdScopeKey];
-
-  ConvertLegacyFlowEventArgs(legacy_args, &event);
-
-  event["args"].removeMember(kLegacyEventArgsKey);
-
-  return event;
-}
-
-util::Status ExportRawEvents(const TraceStorage* storage,
-                             const ArgsBuilder& args_builder,
-                             TraceFormatWriter* writer) {
-  base::Optional<StringId> raw_legacy_event_key_id =
-      storage->string_pool().GetId("track_event.legacy_event");
-  base::Optional<StringId> raw_legacy_system_trace_event_id =
-      storage->string_pool().GetId("chrome_event.legacy_system_trace");
-  base::Optional<StringId> raw_legacy_user_trace_event_id =
-      storage->string_pool().GetId("chrome_event.legacy_user_trace");
-  base::Optional<StringId> raw_chrome_metadata_event_id =
-      storage->string_pool().GetId("chrome_event.metadata");
-
-  const auto& events = storage->raw_events();
-  for (uint32_t i = 0; i < events.raw_event_count(); ++i) {
-    if (raw_legacy_event_key_id &&
-        events.name_ids()[i] == *raw_legacy_event_key_id) {
-      Json::Value event = ConvertLegacyRawEventToJson(storage, args_builder, i);
-      writer->WriteCommonEvent(event);
-    } else if (raw_legacy_system_trace_event_id &&
-               events.name_ids()[i] == *raw_legacy_system_trace_event_id) {
-      Json::Value args = args_builder.GetArgs(events.arg_set_ids()[i]);
-      PERFETTO_DCHECK(args.isMember("data"));
-      writer->AddSystemTraceData(args["data"].asString());
-    } else if (raw_legacy_user_trace_event_id &&
-               events.name_ids()[i] == *raw_legacy_user_trace_event_id) {
-      Json::Value args = args_builder.GetArgs(events.arg_set_ids()[i]);
-      PERFETTO_DCHECK(args.isMember("data"));
-      writer->AddUserTraceData(args["data"].asString());
-    } else if (raw_chrome_metadata_event_id &&
-               events.name_ids()[i] == *raw_chrome_metadata_event_id) {
-      Json::Value args = args_builder.GetArgs(events.arg_set_ids()[i]);
-      writer->MergeMetadata(args);
-    }
-  }
-  return util::OkStatus();
-}
-
-util::Status ExportCpuProfileSamples(const TraceStorage* storage,
-                                     TraceFormatWriter* writer) {
-  const tables::CpuProfileStackSampleTable& samples =
-      storage->cpu_profile_stack_sample_table();
-  for (uint32_t i = 0; i < samples.row_count(); ++i) {
     Json::Value event;
-    event["ts"] = Json::Int64(samples.ts()[i] / 1000);
+    event["ts"] = Json::Int64(events.ts()[index] / 1000);
 
-    UniqueTid utid = static_cast<UniqueTid>(samples.utid()[i]);
-    auto thread = storage->GetThread(utid);
-    event["tid"] = static_cast<int32_t>(thread.tid);
-    if (thread.upid) {
-      event["pid"] =
-          static_cast<int32_t>(storage->GetProcess(*thread.upid).pid);
+    UniqueTid utid = static_cast<UniqueTid>(events.utid()[index]);
+    auto pid_and_tid = UtidToPidAndTid(utid);
+    event["pid"] = Json::Int(pid_and_tid.first);
+    event["tid"] = Json::Int(pid_and_tid.second);
+
+    // Raw legacy events store all other params in the arg set. Make a copy of
+    // the converted args here, parse, and then remove the legacy params.
+    event["args"] = args_builder_.GetArgs(events.arg_set_id()[index]);
+    const Json::Value& legacy_args = event["args"][kLegacyEventArgsKey];
+
+    PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventCategoryKey));
+    event["cat"] = legacy_args[kLegacyEventCategoryKey];
+
+    PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventNameKey));
+    event["name"] = legacy_args[kLegacyEventNameKey];
+
+    PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventPhaseKey));
+    event["ph"] = legacy_args[kLegacyEventPhaseKey];
+
+    // Object snapshot events are supposed to have a mandatory "snapshot" arg,
+    // which may be removed in trace processor if it is empty.
+    if (legacy_args[kLegacyEventPhaseKey] == "O" &&
+        !event["args"].isMember("snapshot")) {
+      event["args"]["snapshot"] = Json::Value(Json::objectValue);
     }
 
-    event["ph"] = "n";
-    event["cat"] = "disabled_by_default-cpu_profiler";
-    event["name"] = "StackCpuSampling";
-    event["s"] = "t";
+    if (legacy_args.isMember(kLegacyEventDurationNsKey))
+      event["dur"] = legacy_args[kLegacyEventDurationNsKey].asInt64() / 1000;
 
-    // Add a dummy thread timestamp to this event to match the format of instant
-    // events. Useful in the UI to view args of a selected group of samples.
-    event["tts"] = Json::Int64(1);
+    if (legacy_args.isMember(kLegacyEventThreadTimestampNsKey)) {
+      event["tts"] =
+          legacy_args[kLegacyEventThreadTimestampNsKey].asInt64() / 1000;
+    }
 
-    // "n"-phase events are nestable async events which get tied together with
-    // their id, so we need to give each one a unique ID as we only
-    // want the samples to show up on their own track in the trace-viewer but
-    // not nested together.
-    static size_t g_id_counter = 0;
-    event["id"] = PrintUint64(++g_id_counter);
+    if (legacy_args.isMember(kLegacyEventThreadDurationNsKey)) {
+      event["tdur"] =
+          legacy_args[kLegacyEventThreadDurationNsKey].asInt64() / 1000;
+    }
 
-    std::vector<std::string> callstack;
-    const auto& callsites = storage->stack_profile_callsite_table();
-    int64_t maybe_callsite_id = samples.callsite_id()[i];
-    PERFETTO_DCHECK(maybe_callsite_id >= 0 &&
-                    maybe_callsite_id < callsites.row_count());
-    while (maybe_callsite_id >= 0) {
-      uint32_t callsite_id = static_cast<uint32_t>(maybe_callsite_id);
+    if (legacy_args.isMember(kLegacyEventThreadInstructionCountKey))
+      event["ticount"] = legacy_args[kLegacyEventThreadInstructionCountKey];
 
-      const auto& frames = storage->stack_profile_frame_table();
-      PERFETTO_DCHECK(callsites.frame_id()[callsite_id] >= 0 &&
-                      callsites.frame_id()[callsite_id] < frames.row_count());
-      uint32_t frame_id =
-          static_cast<uint32_t>(callsites.frame_id()[callsite_id]);
+    if (legacy_args.isMember(kLegacyEventThreadInstructionDeltaKey))
+      event["tidelta"] = legacy_args[kLegacyEventThreadInstructionDeltaKey];
 
-      const auto& mappings = storage->stack_profile_mapping_table();
-      PERFETTO_DCHECK(frames.mapping()[frame_id] >= 0 &&
-                      frames.mapping()[frame_id] < mappings.row_count());
-      uint32_t mapping_id = static_cast<uint32_t>(frames.mapping()[frame_id]);
+    if (legacy_args.isMember(kLegacyEventUseAsyncTtsKey))
+      event["use_async_tts"] = legacy_args[kLegacyEventUseAsyncTtsKey];
 
-      NullTermStringView symbol_name;
-      auto opt_symbol_set_id = frames.symbol_set_id()[frame_id];
-      if (opt_symbol_set_id) {
-        symbol_name = storage->GetString(
-            storage->symbol_table().name()[*opt_symbol_set_id]);
+    if (legacy_args.isMember(kLegacyEventUnscopedIdKey)) {
+      event["id"] =
+          PrintUint64(legacy_args[kLegacyEventUnscopedIdKey].asUInt64());
+    }
+
+    if (legacy_args.isMember(kLegacyEventGlobalIdKey)) {
+      event["id2"]["global"] =
+          PrintUint64(legacy_args[kLegacyEventGlobalIdKey].asUInt64());
+    }
+
+    if (legacy_args.isMember(kLegacyEventLocalIdKey)) {
+      event["id2"]["local"] =
+          PrintUint64(legacy_args[kLegacyEventLocalIdKey].asUInt64());
+    }
+
+    if (legacy_args.isMember(kLegacyEventIdScopeKey))
+      event["scope"] = legacy_args[kLegacyEventIdScopeKey];
+
+    ConvertLegacyFlowEventArgs(legacy_args, &event);
+
+    event["args"].removeMember(kLegacyEventArgsKey);
+
+    return event;
+  }
+
+  util::Status ExportRawEvents() {
+    base::Optional<StringId> raw_legacy_event_key_id =
+        storage_->string_pool().GetId("track_event.legacy_event");
+    base::Optional<StringId> raw_legacy_system_trace_event_id =
+        storage_->string_pool().GetId("chrome_event.legacy_system_trace");
+    base::Optional<StringId> raw_legacy_user_trace_event_id =
+        storage_->string_pool().GetId("chrome_event.legacy_user_trace");
+    base::Optional<StringId> raw_chrome_metadata_event_id =
+        storage_->string_pool().GetId("chrome_event.metadata");
+
+    const auto& events = storage_->raw_table();
+    for (uint32_t i = 0; i < events.row_count(); ++i) {
+      if (raw_legacy_event_key_id &&
+          events.name()[i] == *raw_legacy_event_key_id) {
+        Json::Value event = ConvertLegacyRawEventToJson(i);
+        writer_.WriteCommonEvent(event);
+      } else if (raw_legacy_system_trace_event_id &&
+                 events.name()[i] == *raw_legacy_system_trace_event_id) {
+        Json::Value args = args_builder_.GetArgs(events.arg_set_id()[i]);
+        PERFETTO_DCHECK(args.isMember("data"));
+        writer_.AddSystemTraceData(args["data"].asString());
+      } else if (raw_legacy_user_trace_event_id &&
+                 events.name()[i] == *raw_legacy_user_trace_event_id) {
+        Json::Value args = args_builder_.GetArgs(events.arg_set_id()[i]);
+        PERFETTO_DCHECK(args.isMember("data"));
+        writer_.AddUserTraceData(args["data"].asString());
+      } else if (raw_chrome_metadata_event_id &&
+                 events.name()[i] == *raw_chrome_metadata_event_id) {
+        Json::Value args = args_builder_.GetArgs(events.arg_set_id()[i]);
+        writer_.MergeMetadata(args);
+      }
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ExportCpuProfileSamples() {
+    const tables::CpuProfileStackSampleTable& samples =
+        storage_->cpu_profile_stack_sample_table();
+    for (uint32_t i = 0; i < samples.row_count(); ++i) {
+      Json::Value event;
+      event["ts"] = Json::Int64(samples.ts()[i] / 1000);
+
+      UniqueTid utid = static_cast<UniqueTid>(samples.utid()[i]);
+      auto pid_and_tid = UtidToPidAndTid(utid);
+      event["pid"] = Json::Int(pid_and_tid.first);
+      event["tid"] = Json::Int(pid_and_tid.second);
+
+      event["ph"] = "n";
+      event["cat"] = "disabled_by_default-cpu_profiler";
+      event["name"] = "StackCpuSampling";
+      event["s"] = "t";
+
+      // Add a dummy thread timestamp to this event to match the format of
+      // instant events. Useful in the UI to view args of a selected group of
+      // samples.
+      event["tts"] = Json::Int64(1);
+
+      // "n"-phase events are nestable async events which get tied together with
+      // their id, so we need to give each one a unique ID as we only
+      // want the samples to show up on their own track in the trace-viewer but
+      // not nested together.
+      static size_t g_id_counter = 0;
+      event["id"] = PrintUint64(++g_id_counter);
+
+      std::vector<std::string> callstack;
+      const auto& callsites = storage_->stack_profile_callsite_table();
+      int64_t maybe_callsite_id = samples.callsite_id()[i];
+      PERFETTO_DCHECK(maybe_callsite_id >= 0 &&
+                      maybe_callsite_id < callsites.row_count());
+      while (maybe_callsite_id >= 0) {
+        uint32_t callsite_id = static_cast<uint32_t>(maybe_callsite_id);
+
+        const auto& frames = storage_->stack_profile_frame_table();
+        PERFETTO_DCHECK(callsites.frame_id()[callsite_id] >= 0 &&
+                        callsites.frame_id()[callsite_id] < frames.row_count());
+        uint32_t frame_id =
+            static_cast<uint32_t>(callsites.frame_id()[callsite_id]);
+
+        const auto& mappings = storage_->stack_profile_mapping_table();
+        PERFETTO_DCHECK(frames.mapping()[frame_id] >= 0 &&
+                        frames.mapping()[frame_id] < mappings.row_count());
+        uint32_t mapping_id = static_cast<uint32_t>(frames.mapping()[frame_id]);
+
+        NullTermStringView symbol_name;
+        auto opt_symbol_set_id = frames.symbol_set_id()[frame_id];
+        if (opt_symbol_set_id) {
+          symbol_name = storage_->GetString(
+              storage_->symbol_table().name()[*opt_symbol_set_id]);
+        }
+
+        char frame_entry[1024];
+        snprintf(
+            frame_entry, sizeof(frame_entry), "%s - %s [%s]\n",
+            (symbol_name.empty()
+                 ? PrintUint64(static_cast<uint64_t>(frames.rel_pc()[frame_id]))
+                       .c_str()
+                 : symbol_name.c_str()),
+            GetNonNullString(storage_, mappings.name()[mapping_id]),
+            GetNonNullString(storage_, mappings.build_id()[mapping_id]));
+
+        callstack.emplace_back(frame_entry);
+
+        maybe_callsite_id = callsites.parent_id()[callsite_id];
       }
 
-      char frame_entry[1024];
-      snprintf(
-          frame_entry, sizeof(frame_entry), "%s - %s [%s]\n",
-          (symbol_name.empty()
-               ? PrintUint64(static_cast<uint64_t>(frames.rel_pc()[frame_id]))
-                     .c_str()
-               : symbol_name.c_str()),
-          GetNonNullString(storage, mappings.name()[mapping_id]),
-          GetNonNullString(storage, mappings.build_id()[mapping_id]));
+      std::string merged_callstack;
+      for (auto entry = callstack.rbegin(); entry != callstack.rend();
+           ++entry) {
+        merged_callstack += *entry;
+      }
 
-      callstack.emplace_back(frame_entry);
+      event["args"]["frames"] = merged_callstack;
 
-      maybe_callsite_id = callsites.parent_id()[callsite_id];
+      // TODO(oysteine): Used for backwards compatibility with the memlog
+      // pipeline, should remove once we've switched to looking directly at the
+      // tid.
+      event["args"]["thread_id"] = Json::Int(pid_and_tid.second);
+
+      writer_.WriteCommonEvent(event);
     }
 
-    std::string merged_callstack;
-    for (auto entry = callstack.rbegin(); entry != callstack.rend(); ++entry) {
-      merged_callstack += *entry;
+    return util::OkStatus();
+  }
+
+  util::Status ExportMetadata() {
+    const auto& trace_metadata = storage_->metadata_table();
+    const auto& keys = trace_metadata.name();
+    const auto& int_values = trace_metadata.int_value();
+    const auto& str_values = trace_metadata.str_value();
+
+    // Create a mapping from key string ids to keys.
+    std::unordered_map<StringId, metadata::KeyIDs> key_map;
+    for (uint32_t i = 0; i < metadata::kNumKeys; ++i) {
+      auto id = *storage_->string_pool().GetId(metadata::kNames[i]);
+      key_map[id] = static_cast<metadata::KeyIDs>(i);
     }
 
-    event["args"]["frames"] = merged_callstack;
+    for (uint32_t pos = 0; pos < trace_metadata.row_count(); pos++) {
+      // Cast away from enum type, as otherwise -Wswitch-enum will demand an
+      // exhaustive list of cases, even if there's a default case.
+      metadata::KeyIDs key = key_map[keys[pos]];
+      switch (static_cast<size_t>(key)) {
+        case metadata::benchmark_description:
+          writer_.AppendTelemetryMetadataString(
+              "benchmarkDescriptions",
+              GetNonNullString(storage_, str_values[pos]));
+          break;
 
-    // TODO(oysteine): Used for backwards compatibility with the memlog
-    // pipeline, should remove once we've switched to looking directly at the
-    // tid.
-    event["args"]["thread_id"] = thread.tid;
+        case metadata::benchmark_name:
+          writer_.AppendTelemetryMetadataString(
+              "benchmarks", GetNonNullString(storage_, str_values[pos]));
+          break;
 
-    writer->WriteCommonEvent(event);
-  }
+        case metadata::benchmark_start_time_us:
 
-  return util::OkStatus();
-}
+          writer_.SetTelemetryMetadataTimestamp("benchmarkStart",
+                                                *int_values[pos]);
+          break;
 
-util::Status ExportMetadata(const TraceStorage* storage,
-                            TraceFormatWriter* writer) {
-  const auto& trace_metadata = storage->metadata_table();
-  const auto& keys = trace_metadata.name();
-  const auto& int_values = trace_metadata.int_value();
-  const auto& str_values = trace_metadata.str_value();
+        case metadata::benchmark_had_failures:
+          writer_.AppendTelemetryMetadataBool("hadFailures", *int_values[pos]);
+          break;
 
-  // Create a mapping from key string ids to keys.
-  std::unordered_map<StringId, metadata::KeyIDs> key_map;
-  for (uint32_t i = 0; i < metadata::kNumKeys; ++i) {
-    auto id = *storage->string_pool().GetId(metadata::kNames[i]);
-    key_map[id] = static_cast<metadata::KeyIDs>(i);
-  }
+        case metadata::benchmark_label:
+          writer_.AppendTelemetryMetadataString(
+              "labels", GetNonNullString(storage_, str_values[pos]));
+          break;
 
-  for (uint32_t pos = 0; pos < trace_metadata.row_count(); pos++) {
-    // Cast away from enum type, as otherwise -Wswitch-enum will demand an
-    // exhaustive list of cases, even if there's a default case.
-    metadata::KeyIDs key = key_map[keys[pos]];
-    switch (static_cast<size_t>(key)) {
-      case metadata::benchmark_description:
-        writer->AppendTelemetryMetadataString(
-            "benchmarkDescriptions",
-            GetNonNullString(storage, str_values[pos]));
-        break;
+        case metadata::benchmark_story_name:
+          writer_.AppendTelemetryMetadataString(
+              "stories", GetNonNullString(storage_, str_values[pos]));
+          break;
 
-      case metadata::benchmark_name:
-        writer->AppendTelemetryMetadataString(
-            "benchmarks", GetNonNullString(storage, str_values[pos]));
-        break;
+        case metadata::benchmark_story_run_index:
+          writer_.AppendTelemetryMetadataInt("storysetRepeats",
+                                             *int_values[pos]);
+          break;
 
-      case metadata::benchmark_start_time_us:
+        case metadata::benchmark_story_run_time_us:
+          writer_.SetTelemetryMetadataTimestamp("traceStart", *int_values[pos]);
+          break;
 
-        writer->SetTelemetryMetadataTimestamp("benchmarkStart",
-                                              *int_values[pos]);
-        break;
+        case metadata::benchmark_story_tags:  // repeated
+          writer_.AppendTelemetryMetadataString(
+              "storyTags", GetNonNullString(storage_, str_values[pos]));
+          break;
 
-      case metadata::benchmark_had_failures:
-        writer->AppendTelemetryMetadataBool("hadFailures", *int_values[pos]);
-        break;
-
-      case metadata::benchmark_label:
-        writer->AppendTelemetryMetadataString(
-            "labels", GetNonNullString(storage, str_values[pos]));
-        break;
-
-      case metadata::benchmark_story_name:
-        writer->AppendTelemetryMetadataString(
-            "stories", GetNonNullString(storage, str_values[pos]));
-        break;
-
-      case metadata::benchmark_story_run_index:
-        writer->AppendTelemetryMetadataInt("storysetRepeats", *int_values[pos]);
-        break;
-
-      case metadata::benchmark_story_run_time_us:
-        writer->SetTelemetryMetadataTimestamp("traceStart", *int_values[pos]);
-        break;
-
-      case metadata::benchmark_story_tags:  // repeated
-        writer->AppendTelemetryMetadataString(
-            "storyTags", GetNonNullString(storage, str_values[pos]));
-        break;
-
-      default:
-        PERFETTO_DLOG("Ignoring metadata key %zu", static_cast<size_t>(key));
-        break;
+        default:
+          PERFETTO_DLOG("Ignoring metadata key %zu", static_cast<size_t>(key));
+          break;
+      }
     }
+    return util::OkStatus();
   }
-  return util::OkStatus();
-}
 
-util::Status ExportStats(const TraceStorage* storage,
-                         TraceFormatWriter* writer) {
-  const auto& stats = storage->stats();
+  util::Status ExportStats() {
+    const auto& stats = storage_->stats();
 
-  for (size_t idx = 0; idx < stats::kNumKeys; idx++) {
-    if (stats::kTypes[idx] == stats::kSingle) {
-      writer->SetStats(stats::kNames[idx], stats[idx].value);
-    } else {
-      PERFETTO_DCHECK(stats::kTypes[idx] == stats::kIndexed);
-      writer->SetStats(stats::kNames[idx], stats[idx].indexed_values);
+    for (size_t idx = 0; idx < stats::kNumKeys; idx++) {
+      if (stats::kTypes[idx] == stats::kSingle) {
+        writer_.SetStats(stats::kNames[idx], stats[idx].value);
+      } else {
+        PERFETTO_DCHECK(stats::kTypes[idx] == stats::kIndexed);
+        writer_.SetStats(stats::kNames[idx], stats[idx].indexed_values);
+      }
     }
+
+    return util::OkStatus();
   }
 
-  return util::OkStatus();
-}
+  uint32_t UpidToPid(UniquePid upid) {
+    auto pid_it = upids_to_exported_pids_.find(upid);
+    PERFETTO_DCHECK(pid_it != upids_to_exported_pids_.end());
+    return pid_it->second;
+  }
+
+  std::pair<uint32_t, uint32_t> UtidToPidAndTid(UniqueTid utid) {
+    auto pid_and_tid_it = utids_to_exported_pids_and_tids_.find(utid);
+    PERFETTO_DCHECK(pid_and_tid_it != utids_to_exported_pids_and_tids_.end());
+    return pid_and_tid_it->second;
+  }
+
+  uint32_t NextExportedPidOrTidForDuplicates() {
+    // Ensure that the exported substitute value does not represent a valid
+    // pid/tid. This would be very unlikely in practice.
+    while (IsValidPidOrTid(next_exported_pid_or_tid_for_duplicates_))
+      next_exported_pid_or_tid_for_duplicates_--;
+    return next_exported_pid_or_tid_for_duplicates_--;
+  }
+
+  bool IsValidPidOrTid(uint32_t pid_or_tid) {
+    const auto& process_table = storage_->process_table();
+    for (UniquePid upid = 0; upid < process_table.row_count(); upid++) {
+      if (process_table.pid()[upid] == pid_or_tid)
+        return true;
+    }
+
+    const auto& thread_table = storage_->thread_table();
+    for (UniqueTid utid = 0; utid < thread_table.row_count(); utid++) {
+      if (thread_table.tid()[utid] == pid_or_tid)
+        return true;
+    }
+
+    return false;
+  }
+
+  const TraceStorage* storage_;
+  ArgsBuilder args_builder_;
+  TraceFormatWriter writer_;
+
+  // If a pid/tid is duplicated between two or more  different processes/threads
+  // (pid/tid reuse), we export the subsequent occurrences with different
+  // pids/tids that is visibly different from regular pids/tids - counting down
+  // from uint32_t max.
+  uint32_t next_exported_pid_or_tid_for_duplicates_ =
+      std::numeric_limits<uint32_t>::max();
+
+  std::map<UniquePid, uint32_t> upids_to_exported_pids_;
+  std::map<uint32_t, UniquePid> exported_pids_to_upids_;
+  std::map<UniqueTid, std::pair<uint32_t, uint32_t>>
+      utids_to_exported_pids_and_tids_;
+  std::map<std::pair<uint32_t, uint32_t>, UniqueTid>
+      exported_pids_and_tids_to_utids_;
+};
 
 }  // namespace
 
@@ -1047,40 +1332,9 @@
                         ArgumentFilterPredicate argument_filter,
                         MetadataFilterPredicate metadata_filter,
                         LabelFilterPredicate label_filter) {
-  // TODO(eseckler): Implement argument/metadata/label filtering.
-  TraceFormatWriter writer(output, argument_filter, metadata_filter,
-                           label_filter);
-  ArgsBuilder args_builder(storage);
-
-  util::Status status = ExportThreadNames(storage, &writer);
-  if (!status.ok())
-    return status;
-
-  status = ExportProcessNames(storage, &writer);
-  if (!status.ok())
-    return status;
-
-  status = ExportSlices(storage, args_builder, &writer);
-  if (!status.ok())
-    return status;
-
-  status = ExportRawEvents(storage, args_builder, &writer);
-  if (!status.ok())
-    return status;
-
-  status = ExportCpuProfileSamples(storage, &writer);
-  if (!status.ok())
-    return status;
-
-  status = ExportMetadata(storage, &writer);
-  if (!status.ok())
-    return status;
-
-  status = ExportStats(storage, &writer);
-  if (!status.ok())
-    return status;
-
-  return util::OkStatus();
+  JsonExporter exporter(storage, output, std::move(argument_filter),
+                        std::move(metadata_filter), std::move(label_filter));
+  return exporter.Export();
 }
 
 util::Status ExportJson(TraceProcessorStorage* tp,
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 7cac7cd..14ca221 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -27,6 +27,7 @@
 #include "perfetto/ext/base/temp_file.h"
 #include "src/trace_processor/args_tracker.h"
 #include "src/trace_processor/metadata_tracker.h"
+#include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/track_tracker.h"
 
@@ -65,10 +66,12 @@
 class ExportJsonTest : public ::testing::Test {
  public:
   ExportJsonTest() {
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.storage.reset(new TraceStorage());
     context_.track_tracker.reset(new TrackTracker(&context_));
     context_.metadata_tracker.reset(new MetadataTracker(&context_));
+    context_.process_tracker.reset(new ProcessTracker(&context_));
   }
 
   std::string ToJson(ArgumentFilterPredicate argument_filter = nullptr,
@@ -111,13 +114,12 @@
   const int64_t kThreadDuration = 20000;
   const int64_t kThreadInstructionCount = 30000000;
   const int64_t kThreadInstructionDelta = 30000;
-  const int64_t kThreadID = 100;
+  const uint32_t kThreadID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(kThreadID);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -144,7 +146,7 @@
   EXPECT_EQ(event["tdur"].asInt64(), kThreadDuration / 1000);
   EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
   EXPECT_EQ(event["tidelta"].asInt64(), kThreadInstructionDelta);
-  EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(event["cat"].asString(), kCategory);
   EXPECT_EQ(event["name"].asString(), kName);
   EXPECT_TRUE(event["args"].isObject());
@@ -158,13 +160,12 @@
   const int64_t kThreadDuration = -1;
   const int64_t kThreadInstructionCount = 30000000;
   const int64_t kThreadInstructionDelta = -1;
-  const int64_t kThreadID = 100;
+  const uint32_t kThreadID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(kThreadID);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -191,7 +192,7 @@
   EXPECT_FALSE(event.isMember("tdur"));
   EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
   EXPECT_FALSE(event.isMember("tidelta"));
-  EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(event["cat"].asString(), kCategory);
   EXPECT_EQ(event["name"].asString(), kName);
   EXPECT_TRUE(event["args"].isObject());
@@ -199,12 +200,12 @@
 }
 
 TEST_F(ExportJsonTest, StorageWithThreadName) {
-  const int64_t kThreadID = 100;
+  const uint32_t kThreadID = 100;
   const char* kName = "thread";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(kThreadID);
-  StringId name_id = context_.storage->InternString(base::StringView(kName));
-  context_.storage->GetMutableThread(utid)->name_id = name_id;
+  tables::ThreadTable::Row row(kThreadID);
+  row.name = context_.storage->InternString(base::StringView(kName));
+  context_.storage->mutable_thread_table()->Insert(row);
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -217,18 +218,19 @@
 
   Json::Value event = result["traceEvents"][0];
   EXPECT_EQ(event["ph"].asString(), "M");
-  EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(event["name"].asString(), "thread_name");
   EXPECT_EQ(event["args"]["name"].asString(), kName);
 }
 
-TEST_F(ExportJsonTest, WrongTrackTypeIgnored) {
+TEST_F(ExportJsonTest, SystemEventsIgnored) {
   constexpr int64_t kCookie = 22;
   TrackId track = context_.track_tracker->InternAndroidAsyncTrack(
-      /*name=*/0, /*upid=*/0, kCookie);
+      /*name=*/kNullStringId, /*upid=*/0, kCookie);
   context_.args_tracker->Flush();  // Flush track args.
 
-  StringId cat_id = context_.storage->InternString("cat");
+  // System events have no category.
+  StringId cat_id = kNullStringId;
   StringId name_id = context_.storage->InternString("name");
   context_.storage->mutable_slice_table()->Insert(
       {0, 0, track.value, cat_id, name_id, 0, 0, 0});
@@ -371,16 +373,16 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-      0, storage->InternString("chrome_event.metadata"), 0, 0);
+  RawId id = storage->mutable_raw_table()->Insert(
+      {0, storage->InternString("chrome_event.metadata"), 0, 0});
 
   StringId name1_id = storage->InternString(base::StringView(kName1));
   StringId name2_id = storage->InternString(base::StringView(kName2));
   StringId value1_id = storage->InternString(base::StringView(kValue1));
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, name1_id, name1_id,
-                                Variadic::String(value1_id));
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, name2_id, name2_id,
-                                Variadic::Integer(kValue2));
+
+  context_.args_tracker->AddArgsTo(id)
+      .AddArg(name1_id, Variadic::String(value1_id))
+      .AddArg(name2_id, Variadic::Integer(kValue2));
   context_.args_tracker->Flush();
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -402,9 +404,8 @@
   const char* kName = "name";
   const char* kSrc = "source_file.cc";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -415,11 +416,11 @@
       base::StringView("task.posted_from.file_name"));
   StringId arg_value_id =
       context_.storage->InternString(base::StringView(kSrc));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::String(arg_value_id);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -447,20 +448,17 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  UniqueTid utid = storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = storage->InternString(base::StringView(kCategory));
   StringId name_id = storage->InternString(base::StringView(kName));
   SliceId id = storage->mutable_slice_table()->Insert(
       {0, 0, track.value, cat_id, name_id, 0, 0, 0});
-  uint32_t row = *storage->slice_table().id().IndexOf(id);
+  auto inserter = context_.args_tracker->AddArgsTo(id);
 
   auto add_arg = [&](const char* key, Variadic value) {
-    StringId key_id = storage->InternString(key);
-    context_.args_tracker->AddArg(TableId::kNestableSlices, row, key_id, key_id,
-                                  value);
+    inserter.AddArg(storage->InternString(key), value);
   };
 
   add_arg("legacy_event.bind_id", Variadic::UnsignedInteger(kBindId));
@@ -497,9 +495,8 @@
   const char* kName = "name";
   double kValues[] = {1.234, 2.345};
 
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -512,16 +509,15 @@
       base::StringView("debug.draw_duration_ms[0]"));
   StringId arg_key1_id = context_.storage->InternString(
       base::StringView("debug.draw_duration_ms[1]"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_flat_key_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Real(kValues[0]);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_flat_key_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Real(kValues[1]);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -547,9 +543,8 @@
   uint64_t kValue0 = 1;
   uint64_t kValue1 = std::numeric_limits<uint64_t>::max();
 
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -560,16 +555,15 @@
       context_.storage->InternString(base::StringView("arg0"));
   StringId arg_key1_id =
       context_.storage->InternString(base::StringView("arg1"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_key0_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Pointer(kValue0);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_key1_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Pointer(kValue1);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -593,9 +587,8 @@
   const char* kName = "name";
   int kValues[] = {123, 234};
 
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -608,16 +601,15 @@
       context_.storage->InternString(base::StringView("a[0].b"));
   StringId arg_key1_id =
       context_.storage->InternString(base::StringView("a[1].b"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_flat_key_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Integer(kValues[0]);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_flat_key_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Integer(kValues[1]);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -642,9 +634,8 @@
   const char* kName = "name";
   int kValues[] = {123, 234};
 
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -657,16 +648,15 @@
       context_.storage->InternString(base::StringView("a[0][0]"));
   StringId arg_key1_id =
       context_.storage->InternString(base::StringView("a[0][1]"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_flat_key_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Integer(kValues[0]);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_flat_key_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Integer(kValues[1]);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -691,9 +681,8 @@
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -703,11 +692,11 @@
   StringId arg_key_id = context_.storage->InternString(base::StringView("a"));
   StringId arg_value_id =
       context_.storage->InternString(base::StringView("{\"b\":123}"));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::Json(arg_value_id);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -727,9 +716,12 @@
 
 TEST_F(ExportJsonTest, InstantEvent) {
   const int64_t kTimestamp = 10000000;
+  const int64_t kTimestamp2 = 10001000;
+  const int64_t kTimestamp3 = 10002000;
   const char* kCategory = "cat";
   const char* kName = "name";
 
+  // Global legacy track.
   TrackId track =
       context_.track_tracker->GetOrCreateLegacyChromeGlobalInstantTrack();
   context_.args_tracker->Flush();  // Flush track args.
@@ -738,6 +730,19 @@
   context_.storage->mutable_slice_table()->Insert(
       {kTimestamp, 0, track.value, cat_id, name_id, 0, 0, 0});
 
+  // Global track.
+  TrackId track2 = context_.track_tracker->GetOrCreateDefaultDescriptorTrack();
+  context_.args_tracker->Flush();  // Flush track args.
+  context_.storage->mutable_slice_table()->Insert(
+      {kTimestamp2, 0, track2.value, cat_id, name_id, 0, 0, 0});
+
+  // Async event track.
+  context_.track_tracker->ReserveDescriptorChildTrack(1234, 0);
+  TrackId track3 = *context_.track_tracker->GetDescriptorTrack(1234);
+  context_.args_tracker->Flush();  // Flush track args.
+  context_.storage->mutable_slice_table()->Insert(
+      {kTimestamp3, 0, track3.value, cat_id, name_id, 0, 0, 0});
+
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
   util::Status status = ExportJson(context_.storage.get(), output);
@@ -745,7 +750,7 @@
   EXPECT_TRUE(status.ok());
 
   Json::Value result = ToJsonValue(ReadFile(output));
-  EXPECT_EQ(result["traceEvents"].size(), 1u);
+  EXPECT_EQ(result["traceEvents"].size(), 3u);
 
   Json::Value event = result["traceEvents"][0];
   EXPECT_EQ(event["ph"].asString(), "I");
@@ -753,17 +758,30 @@
   EXPECT_EQ(event["s"].asString(), "g");
   EXPECT_EQ(event["cat"].asString(), kCategory);
   EXPECT_EQ(event["name"].asString(), kName);
+
+  Json::Value event2 = result["traceEvents"][1];
+  EXPECT_EQ(event2["ph"].asString(), "I");
+  EXPECT_EQ(event2["ts"].asInt64(), kTimestamp2 / 1000);
+  EXPECT_EQ(event2["s"].asString(), "g");
+  EXPECT_EQ(event2["cat"].asString(), kCategory);
+  EXPECT_EQ(event2["name"].asString(), kName);
+
+  Json::Value event3 = result["traceEvents"][2];
+  EXPECT_EQ(event3["ph"].asString(), "n");
+  EXPECT_EQ(event3["ts"].asInt64(), kTimestamp3 / 1000);
+  EXPECT_EQ(event3["id"].asString(), "0x2");
+  EXPECT_EQ(event3["cat"].asString(), kCategory);
+  EXPECT_EQ(event3["name"].asString(), kName);
 }
 
 TEST_F(ExportJsonTest, InstantEventOnThread) {
   const int64_t kTimestamp = 10000000;
-  const int64_t kThreadID = 100;
+  const uint32_t kThreadID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(kThreadID);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -780,7 +798,7 @@
   EXPECT_EQ(result["traceEvents"].size(), 1u);
 
   Json::Value event = result["traceEvents"][0];
-  EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(event["ph"].asString(), "I");
   EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
   EXPECT_EQ(event["s"].asString(), "t");
@@ -788,41 +806,62 @@
   EXPECT_EQ(event["name"].asString(), kName);
 }
 
-TEST_F(ExportJsonTest, AsyncEvents) {
-  const int64_t kTimestamp = 10000000;
-  const int64_t kDuration = 100000;
-  const int64_t kProcessID = 100;
-  const char* kCategory = "cat";
-  const char* kName = "name";
-  const char* kName2 = "name2";
-  const char* kArgName = "arg_name";
-  const int kArgValue = 123;
+TEST_F(ExportJsonTest, DuplicatePidAndTid) {
+  UniqueTid upid1 = context_.process_tracker->StartNewProcess(
+      base::nullopt, base::nullopt, 1, kNullStringId);
+  UniqueTid utid1a = context_.process_tracker->UpdateThread(1, 1);
+  UniqueTid utid1b = context_.process_tracker->UpdateThread(2, 1);
+  UniqueTid utid1c =
+      context_.process_tracker->StartNewThread(base::nullopt, 2, kNullStringId);
+  // Associate the new thread with its process.
+  ASSERT_EQ(utid1c, context_.process_tracker->UpdateThread(2, 1));
 
-  UniquePid upid = context_.storage->AddEmptyProcess(kProcessID);
-  StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
-  StringId name_id = context_.storage->InternString(base::StringView(kName));
-  StringId name2_id = context_.storage->InternString(base::StringView(kName2));
+  UniqueTid upid2 = context_.process_tracker->StartNewProcess(
+      base::nullopt, base::nullopt, 1, kNullStringId);
+  UniqueTid utid2a = context_.process_tracker->UpdateThread(1, 1);
+  UniqueTid utid2b = context_.process_tracker->UpdateThread(2, 1);
 
-  constexpr int64_t kSourceId = 235;
-  TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
-      name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
-      /*source_scope=*/0);
+  ASSERT_NE(upid1, upid2);
+  ASSERT_NE(utid1b, utid1c);
+  ASSERT_NE(utid1a, utid2a);
+  ASSERT_NE(utid1b, utid2b);
+  ASSERT_NE(utid1c, utid2b);
+
+  ASSERT_EQ(upid1, *context_.storage->thread_table().upid()[utid1a]);
+  ASSERT_EQ(upid1, *context_.storage->thread_table().upid()[utid1b]);
+  ASSERT_EQ(upid1, *context_.storage->thread_table().upid()[utid1c]);
+  ASSERT_EQ(upid2, *context_.storage->thread_table().upid()[utid2a]);
+  ASSERT_EQ(upid2, *context_.storage->thread_table().upid()[utid2b]);
+
+  TrackId track1a = context_.track_tracker->InternThreadTrack(utid1a);
+  TrackId track1b = context_.track_tracker->InternThreadTrack(utid1b);
+  TrackId track1c = context_.track_tracker->InternThreadTrack(utid1c);
+  TrackId track2a = context_.track_tracker->InternThreadTrack(utid2a);
+  TrackId track2b = context_.track_tracker->InternThreadTrack(utid2b);
   context_.args_tracker->Flush();  // Flush track args.
 
-  context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
-  StringId arg_key_id =
-      context_.storage->InternString(base::StringView(kArgName));
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = arg_key_id;
-  arg.key = arg_key_id;
-  arg.value = Variadic::Integer(kArgValue);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-  context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
+  StringId cat_id = context_.storage->InternString(base::StringView("cat"));
+  StringId name1a_id =
+      context_.storage->InternString(base::StringView("name1a"));
+  StringId name1b_id =
+      context_.storage->InternString(base::StringView("name1b"));
+  StringId name1c_id =
+      context_.storage->InternString(base::StringView("name1c"));
+  StringId name2a_id =
+      context_.storage->InternString(base::StringView("name2a"));
+  StringId name2b_id =
+      context_.storage->InternString(base::StringView("name2b"));
 
-  // Child event with same timestamps as first one.
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name2_id, 0, 0, 0});
+      {10000, 0, track1a.value, cat_id, name1a_id, 0, 0, 0});
+  context_.storage->mutable_slice_table()->Insert(
+      {20000, 1000, track1b.value, cat_id, name1b_id, 0, 0, 0});
+  context_.storage->mutable_slice_table()->Insert(
+      {30000, 0, track1c.value, cat_id, name1c_id, 0, 0, 0});
+  context_.storage->mutable_slice_table()->Insert(
+      {40000, 0, track2a.value, cat_id, name2a_id, 0, 0, 0});
+  context_.storage->mutable_slice_table()->Insert(
+      {50000, 1000, track2b.value, cat_id, name2b_id, 0, 0, 0});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -831,12 +870,113 @@
   EXPECT_TRUE(status.ok());
 
   Json::Value result = ToJsonValue(ReadFile(output));
-  EXPECT_EQ(result["traceEvents"].size(), 4u);
+  EXPECT_EQ(result["traceEvents"].size(), 5u);
+
+  EXPECT_EQ(result["traceEvents"][0]["pid"].asInt(), 1);
+  EXPECT_EQ(result["traceEvents"][0]["tid"].asInt(), 1);
+  EXPECT_EQ(result["traceEvents"][0]["ph"].asString(), "I");
+  EXPECT_EQ(result["traceEvents"][0]["ts"].asInt64(), 10);
+  EXPECT_EQ(result["traceEvents"][0]["cat"].asString(), "cat");
+  EXPECT_EQ(result["traceEvents"][0]["name"].asString(), "name1a");
+
+  EXPECT_EQ(result["traceEvents"][1]["pid"].asInt(), 1);
+  EXPECT_EQ(result["traceEvents"][1]["tid"].asInt(), 2);
+  EXPECT_EQ(result["traceEvents"][1]["ph"].asString(), "X");
+  EXPECT_EQ(result["traceEvents"][1]["ts"].asInt64(), 20);
+  EXPECT_EQ(result["traceEvents"][1]["dur"].asInt64(), 1);
+  EXPECT_EQ(result["traceEvents"][1]["cat"].asString(), "cat");
+  EXPECT_EQ(result["traceEvents"][1]["name"].asString(), "name1b");
+
+  EXPECT_EQ(result["traceEvents"][2]["pid"].asInt(), 1);
+  EXPECT_EQ(result["traceEvents"][2]["tid"].asInt(),
+            static_cast<int>(std::numeric_limits<uint32_t>::max() - 1u));
+  EXPECT_EQ(result["traceEvents"][2]["ph"].asString(), "I");
+  EXPECT_EQ(result["traceEvents"][2]["ts"].asInt64(), 30);
+  EXPECT_EQ(result["traceEvents"][2]["cat"].asString(), "cat");
+  EXPECT_EQ(result["traceEvents"][2]["name"].asString(), "name1c");
+
+  EXPECT_EQ(result["traceEvents"][3]["pid"].asInt(),
+            static_cast<int>(std::numeric_limits<uint32_t>::max()));
+  EXPECT_EQ(result["traceEvents"][3]["tid"].asInt(), 1);
+  EXPECT_EQ(result["traceEvents"][3]["ph"].asString(), "I");
+  EXPECT_EQ(result["traceEvents"][3]["ts"].asInt64(), 40);
+  EXPECT_EQ(result["traceEvents"][3]["cat"].asString(), "cat");
+  EXPECT_EQ(result["traceEvents"][3]["name"].asString(), "name2a");
+
+  EXPECT_EQ(result["traceEvents"][4]["pid"].asInt(),
+            static_cast<int>(std::numeric_limits<uint32_t>::max()));
+  EXPECT_EQ(result["traceEvents"][4]["tid"].asInt(), 2);
+  EXPECT_EQ(result["traceEvents"][4]["ph"].asString(), "X");
+  EXPECT_EQ(result["traceEvents"][4]["ts"].asInt64(), 50);
+  EXPECT_EQ(result["traceEvents"][1]["dur"].asInt64(), 1);
+  EXPECT_EQ(result["traceEvents"][4]["cat"].asString(), "cat");
+  EXPECT_EQ(result["traceEvents"][4]["name"].asString(), "name2b");
+}
+
+TEST_F(ExportJsonTest, AsyncEvents) {
+  const int64_t kTimestamp = 10000000;
+  const int64_t kDuration = 100000;
+  const int64_t kTimestamp3 = 10005000;
+  const int64_t kDuration3 = 100000;
+  const uint32_t kProcessID = 100;
+  const char* kCategory = "cat";
+  const char* kName = "name";
+  const char* kName2 = "name2";
+  const char* kName3 = "name3";
+  const char* kArgName = "arg_name";
+  const int kArgValue = 123;
+
+  UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
+  StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
+  StringId name_id = context_.storage->InternString(base::StringView(kName));
+  StringId name2_id = context_.storage->InternString(base::StringView(kName2));
+  StringId name3_id = context_.storage->InternString(base::StringView(kName3));
+
+  constexpr int64_t kSourceId = 235;
+  TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
+      name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
+      /*source_scope=*/kNullStringId);
+  constexpr int64_t kSourceId2 = 236;
+  TrackId track2 = context_.track_tracker->InternLegacyChromeAsyncTrack(
+      name3_id, upid, kSourceId2, /*source_id_is_process_scoped=*/true,
+      /*source_scope=*/kNullStringId);
+  context_.args_tracker->Flush();  // Flush track args.
+
+  context_.storage->mutable_slice_table()->Insert(
+      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+  StringId arg_key_id =
+      context_.storage->InternString(base::StringView(kArgName));
+  GlobalArgsTracker::Arg arg;
+  arg.flat_key = arg_key_id;
+  arg.key = arg_key_id;
+  arg.value = Variadic::Integer(kArgValue);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
+  context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
+
+  // Child event with same timestamps as first one.
+  context_.storage->mutable_slice_table()->Insert(
+      {kTimestamp, kDuration, track.value, cat_id, name2_id, 0, 0, 0});
+
+  // Another overlapping async event on a different track.
+  context_.storage->mutable_slice_table()->Insert(
+      {kTimestamp3, kDuration3, track2.value, cat_id, name3_id, 0, 0, 0});
+
+  base::TempFile temp_file = base::TempFile::Create();
+  FILE* output = fopen(temp_file.path().c_str(), "w+");
+  util::Status status = ExportJson(context_.storage.get(), output);
+
+  EXPECT_TRUE(status.ok());
+
+  Json::Value result = ToJsonValue(ReadFile(output));
+  EXPECT_EQ(result["traceEvents"].size(), 6u);
+
+  // Events should be sorted by timestamp, with child slice's end before its
+  // parent.
 
   Json::Value begin_event1 = result["traceEvents"][0];
   EXPECT_EQ(begin_event1["ph"].asString(), "b");
   EXPECT_EQ(begin_event1["ts"].asInt64(), kTimestamp / 1000);
-  EXPECT_EQ(begin_event1["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(begin_event1["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(begin_event1["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(begin_event1["cat"].asString(), kCategory);
   EXPECT_EQ(begin_event1["name"].asString(), kName);
@@ -847,7 +987,7 @@
   Json::Value begin_event2 = result["traceEvents"][1];
   EXPECT_EQ(begin_event2["ph"].asString(), "b");
   EXPECT_EQ(begin_event2["ts"].asInt64(), kTimestamp / 1000);
-  EXPECT_EQ(begin_event2["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(begin_event2["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(begin_event2["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(begin_event2["cat"].asString(), kCategory);
   EXPECT_EQ(begin_event2["name"].asString(), kName2);
@@ -856,22 +996,34 @@
   EXPECT_FALSE(begin_event2.isMember("tts"));
   EXPECT_FALSE(begin_event2.isMember("use_async_tts"));
 
+  Json::Value begin_event3 = result["traceEvents"][2];
+  EXPECT_EQ(begin_event3["ph"].asString(), "b");
+  EXPECT_EQ(begin_event3["ts"].asInt64(), kTimestamp3 / 1000);
+  EXPECT_EQ(begin_event3["pid"].asInt(), static_cast<int>(kProcessID));
+  EXPECT_EQ(begin_event3["id2"]["local"].asString(), "0xec");
+  EXPECT_EQ(begin_event3["cat"].asString(), kCategory);
+  EXPECT_EQ(begin_event3["name"].asString(), kName3);
+  EXPECT_TRUE(begin_event3["args"].isObject());
+  EXPECT_EQ(begin_event3["args"].size(), 0u);
+  EXPECT_FALSE(begin_event3.isMember("tts"));
+  EXPECT_FALSE(begin_event3.isMember("use_async_tts"));
+
   Json::Value end_event2 = result["traceEvents"][3];
   EXPECT_EQ(end_event2["ph"].asString(), "e");
   EXPECT_EQ(end_event2["ts"].asInt64(), (kTimestamp + kDuration) / 1000);
-  EXPECT_EQ(end_event2["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(end_event2["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(end_event2["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(end_event2["cat"].asString(), kCategory);
-  EXPECT_EQ(end_event2["name"].asString(), kName);
+  EXPECT_EQ(end_event2["name"].asString(), kName2);
   EXPECT_TRUE(end_event2["args"].isObject());
   EXPECT_EQ(end_event2["args"].size(), 0u);
   EXPECT_FALSE(end_event2.isMember("tts"));
   EXPECT_FALSE(end_event2.isMember("use_async_tts"));
 
-  Json::Value end_event1 = result["traceEvents"][3];
+  Json::Value end_event1 = result["traceEvents"][4];
   EXPECT_EQ(end_event1["ph"].asString(), "e");
   EXPECT_EQ(end_event1["ts"].asInt64(), (kTimestamp + kDuration) / 1000);
-  EXPECT_EQ(end_event1["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(end_event1["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(end_event1["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(end_event1["cat"].asString(), kCategory);
   EXPECT_EQ(end_event1["name"].asString(), kName);
@@ -879,6 +1031,18 @@
   EXPECT_EQ(end_event1["args"].size(), 0u);
   EXPECT_FALSE(end_event1.isMember("tts"));
   EXPECT_FALSE(end_event1.isMember("use_async_tts"));
+
+  Json::Value end_event3 = result["traceEvents"][5];
+  EXPECT_EQ(end_event3["ph"].asString(), "e");
+  EXPECT_EQ(end_event3["ts"].asInt64(), (kTimestamp3 + kDuration3) / 1000);
+  EXPECT_EQ(end_event3["pid"].asInt(), static_cast<int>(kProcessID));
+  EXPECT_EQ(end_event3["id2"]["local"].asString(), "0xec");
+  EXPECT_EQ(end_event3["cat"].asString(), kCategory);
+  EXPECT_EQ(end_event3["name"].asString(), kName3);
+  EXPECT_TRUE(end_event3["args"].isObject());
+  EXPECT_EQ(end_event3["args"].size(), 0u);
+  EXPECT_FALSE(end_event3.isMember("tts"));
+  EXPECT_FALSE(end_event3.isMember("use_async_tts"));
 }
 
 TEST_F(ExportJsonTest, AsyncEventWithThreadTimestamp) {
@@ -886,18 +1050,18 @@
   const int64_t kDuration = 100000;
   const int64_t kThreadTimestamp = 10000001;
   const int64_t kThreadDuration = 99998;
-  const int64_t kProcessID = 100;
+  const uint32_t kProcessID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniquePid upid = context_.storage->AddEmptyProcess(kProcessID);
+  UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
 
   constexpr int64_t kSourceId = 235;
   TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
       name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
-      /*source_scope=*/0);
+      /*source_scope=*/kNullStringId);
   context_.args_tracker->Flush();  // Flush track args.
 
   auto slice_id = context_.storage->mutable_slice_table()->Insert(
@@ -919,7 +1083,7 @@
   EXPECT_EQ(begin_event["ts"].asInt64(), kTimestamp / 1000);
   EXPECT_EQ(begin_event["tts"].asInt64(), kThreadTimestamp / 1000);
   EXPECT_EQ(begin_event["use_async_tts"].asInt(), 1);
-  EXPECT_EQ(begin_event["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(begin_event["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(begin_event["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(begin_event["cat"].asString(), kCategory);
   EXPECT_EQ(begin_event["name"].asString(), kName);
@@ -930,7 +1094,7 @@
   EXPECT_EQ(end_event["tts"].asInt64(),
             (kThreadTimestamp + kThreadDuration) / 1000);
   EXPECT_EQ(end_event["use_async_tts"].asInt(), 1);
-  EXPECT_EQ(end_event["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(end_event["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(end_event["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(end_event["cat"].asString(), kCategory);
   EXPECT_EQ(end_event["name"].asString(), kName);
@@ -941,18 +1105,18 @@
   const int64_t kDuration = -1;
   const int64_t kThreadTimestamp = 10000001;
   const int64_t kThreadDuration = -1;
-  const int64_t kProcessID = 100;
+  const uint32_t kProcessID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniquePid upid = context_.storage->AddEmptyProcess(kProcessID);
+  UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
 
   constexpr int64_t kSourceId = 235;
   TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
       name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
-      /*source_scope=*/0);
+      /*source_scope=*/kNullStringId);
   context_.args_tracker->Flush();  // Flush track args.
 
   auto slice_id = context_.storage->mutable_slice_table()->Insert(
@@ -974,7 +1138,7 @@
   EXPECT_EQ(begin_event["ts"].asInt64(), kTimestamp / 1000);
   EXPECT_EQ(begin_event["tts"].asInt64(), kThreadTimestamp / 1000);
   EXPECT_EQ(begin_event["use_async_tts"].asInt(), 1);
-  EXPECT_EQ(begin_event["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(begin_event["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(begin_event["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(begin_event["cat"].asString(), kCategory);
   EXPECT_EQ(begin_event["name"].asString(), kName);
@@ -982,31 +1146,31 @@
 
 TEST_F(ExportJsonTest, AsyncInstantEvent) {
   const int64_t kTimestamp = 10000000;
-  const int64_t kProcessID = 100;
+  const uint32_t kProcessID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
   const char* kArgName = "arg_name";
   const int kArgValue = 123;
 
-  UniquePid upid = context_.storage->AddEmptyProcess(kProcessID);
+  UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
 
   constexpr int64_t kSourceId = 235;
   TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
       name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
-      /*source_scope=*/0);
+      /*source_scope=*/kNullStringId);
   context_.args_tracker->Flush();  // Flush track args.
 
   context_.storage->mutable_slice_table()->Insert(
       {kTimestamp, 0, track.value, cat_id, name_id, 0, 0, 0});
   StringId arg_key_id =
       context_.storage->InternString(base::StringView("arg_name"));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::Integer(kArgValue);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -1021,7 +1185,7 @@
   Json::Value event = result["traceEvents"][0];
   EXPECT_EQ(event["ph"].asString(), "n");
   EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
-  EXPECT_EQ(event["pid"].asInt64(), kProcessID);
+  EXPECT_EQ(event["pid"].asInt(), static_cast<int>(kProcessID));
   EXPECT_EQ(event["id2"]["local"].asString(), "0xeb");
   EXPECT_EQ(event["cat"].asString(), kCategory);
   EXPECT_EQ(event["name"].asString(), kName);
@@ -1035,8 +1199,8 @@
   const int64_t kThreadDuration = 20000;
   const int64_t kThreadInstructionCount = 30000000;
   const int64_t kThreadInstructionDelta = 30000;
-  const int64_t kProcessID = 100;
-  const int64_t kThreadID = 200;
+  const uint32_t kProcessID = 100;
+  const uint32_t kThreadID = 200;
   const char* kCategory = "cat";
   const char* kName = "name";
   const char* kPhase = "?";
@@ -1049,18 +1213,18 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  UniquePid upid = storage->AddEmptyProcess(kProcessID);
-  UniqueTid utid = storage->AddEmptyThread(kThreadID);
-  storage->GetMutableThread(utid)->upid = upid;
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
+  UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
+  context_.storage->mutable_thread_table()->mutable_upid()->Set(utid, upid);
 
-  uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-      kTimestamp, storage->InternString("track_event.legacy_event"), /*cpu=*/0,
-      utid);
+  RawId id = storage->mutable_raw_table()->Insert(
+      {kTimestamp, storage->InternString("track_event.legacy_event"), /*cpu=*/0,
+       utid});
+  auto inserter = context_.args_tracker->AddArgsTo(id);
 
   auto add_arg = [&](const char* key, Variadic value) {
     StringId key_id = storage->InternString(key);
-    context_.args_tracker->AddArg(TableId::kRawEvents, row, key_id, key_id,
-                                  value);
+    inserter.AddArg(key_id, value);
   };
 
   StringId cat_id = storage->InternString(base::StringView(kCategory));
@@ -1109,7 +1273,7 @@
   EXPECT_EQ(event["tdur"].asInt64(), kThreadDuration / 1000);
   EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
   EXPECT_EQ(event["tidelta"].asInt64(), kThreadInstructionDelta);
-  EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(event["cat"].asString(), kCategory);
   EXPECT_EQ(event["name"].asString(), kName);
   EXPECT_EQ(event["use_async_tts"].asInt(), 1);
@@ -1129,25 +1293,25 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-      0, storage->InternString("chrome_event.legacy_system_trace"), 0, 0);
+  RawId id = storage->mutable_raw_table()->Insert(
+      {0, storage->InternString("chrome_event.legacy_system_trace"), 0, 0});
+  auto inserter = context_.args_tracker->AddArgsTo(id);
 
   StringId data_id = storage->InternString("data");
   StringId ftrace_data_id = storage->InternString(kLegacyFtraceData);
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, data_id, data_id,
-                                Variadic::String(ftrace_data_id));
+  inserter.AddArg(data_id, Variadic::String(ftrace_data_id));
 
-  row = storage->mutable_raw_events()->AddRawEvent(
-      0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0);
+  id = storage->mutable_raw_table()->Insert(
+      {0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0});
+  inserter = context_.args_tracker->AddArgsTo(id);
   StringId json_data1_id = storage->InternString(kLegacyJsonData1);
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, data_id, data_id,
-                                Variadic::String(json_data1_id));
+  inserter.AddArg(data_id, Variadic::String(json_data1_id));
 
-  row = storage->mutable_raw_events()->AddRawEvent(
-      0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0);
+  id = storage->mutable_raw_table()->Insert(
+      {0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0});
+  inserter = context_.args_tracker->AddArgsTo(id);
   StringId json_data2_id = storage->InternString(kLegacyJsonData2);
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, data_id, data_id,
-                                Variadic::String(json_data2_id));
+  inserter.AddArg(data_id, Variadic::String(json_data2_id));
 
   context_.args_tracker->Flush();
 
@@ -1166,15 +1330,15 @@
 }
 
 TEST_F(ExportJsonTest, CpuProfileEvent) {
-  const int64_t kProcessID = 100;
-  const int64_t kThreadID = 200;
+  const uint32_t kProcessID = 100;
+  const uint32_t kThreadID = 200;
   const int64_t kTimestamp = 10000000;
 
   TraceStorage* storage = context_.storage.get();
 
-  UniquePid upid = storage->AddEmptyProcess(kProcessID);
-  UniqueTid utid = storage->AddEmptyThread(kThreadID);
-  storage->GetMutableThread(utid)->upid = upid;
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
+  UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
+  context_.storage->mutable_thread_table()->mutable_upid()->Set(utid, upid);
 
   auto module_id_1 = storage->mutable_stack_profile_mapping_table()->Insert(
       {storage->InternString("foo_module_id"), 0, 0, 0, 0, 0,
@@ -1186,10 +1350,11 @@
 
   // TODO(140860736): Once we support null values for
   // stack_profile_frame.symbol_set_id remove this hack
-  storage->mutable_symbol_table()->Insert({0, 0, 0, 0});
+  storage->mutable_symbol_table()->Insert({0, kNullStringId, kNullStringId, 0});
 
   auto* frames = storage->mutable_stack_profile_frame_table();
-  auto frame_id_1 = frames->Insert({/*name_id=*/0, module_id_1.value, 0x42});
+  auto frame_id_1 =
+      frames->Insert({/*name_id=*/kNullStringId, module_id_1.value, 0x42});
   uint32_t frame_row_1 = *frames->id().IndexOf(frame_id_1);
 
   uint32_t symbol_set_id = storage->symbol_table().row_count();
@@ -1198,7 +1363,8 @@
        storage->InternString("foo_file"), 66});
   frames->mutable_symbol_set_id()->Set(frame_row_1, symbol_set_id);
 
-  auto frame_id_2 = frames->Insert({/*name_id=*/0, module_id_2.value, 0x4242});
+  auto frame_id_2 =
+      frames->Insert({/*name_id=*/kNullStringId, module_id_2.value, 0x4242});
   uint32_t frame_row_2 = *frames->id().IndexOf(frame_id_2);
 
   symbol_set_id = storage->symbol_table().row_count();
@@ -1231,7 +1397,7 @@
   EXPECT_EQ(event["ph"].asString(), "n");
   EXPECT_EQ(event["id"].asString(), "0x1");
   EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
-  EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(event["cat"].asString(), "disabled_by_default-cpu_profiler");
   EXPECT_EQ(event["name"].asString(), "StackCpuSampling");
   EXPECT_EQ(event["s"].asString(), "t");
@@ -1241,9 +1407,8 @@
 }
 
 TEST_F(ExportJsonTest, ArgumentFilter) {
-  UniqueTid utid = context_.storage->AddEmptyThread(0);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
 
   StringId cat_id = context_.storage->InternString(base::StringView("cat"));
@@ -1255,18 +1420,16 @@
   StringId arg2_id = context_.storage->InternString(base::StringView("arg2"));
   StringId val_id = context_.storage->InternString(base::StringView("val"));
 
-  std::array<uint32_t, 3> slice_rows;
+  std::vector<ArgsTracker::BoundInserter> slice_inserters;
   for (size_t i = 0; i < name_ids.size(); i++) {
     auto slice_id = context_.storage->mutable_slice_table()->Insert(
         {0, 0, track.value, cat_id, name_ids[i], 0, 0, 0});
-    slice_rows[i] = *context_.storage->slice_table().id().IndexOf(slice_id);
+    slice_inserters.emplace_back(context_.args_tracker->AddArgsTo(slice_id));
   }
 
-  for (uint32_t row : slice_rows) {
-    context_.args_tracker->AddArg(TableId::kNestableSlices, row, arg1_id,
-                                  arg1_id, Variadic::Integer(5));
-    context_.args_tracker->AddArg(TableId::kNestableSlices, row, arg2_id,
-                                  arg2_id, Variadic::String(val_id));
+  for (auto& inserter : slice_inserters) {
+    inserter.AddArg(arg1_id, Variadic::Integer(5))
+        .AddArg(arg2_id, Variadic::String(val_id));
   }
   context_.args_tracker->Flush();
 
@@ -1320,16 +1483,16 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-      0, storage->InternString("chrome_event.metadata"), 0, 0);
+  RawId id = storage->mutable_raw_table()->Insert(
+      {0, storage->InternString("chrome_event.metadata"), 0, 0});
 
   StringId name1_id = storage->InternString(base::StringView(kName1));
   StringId name2_id = storage->InternString(base::StringView(kName2));
   StringId value1_id = storage->InternString(base::StringView(kValue1));
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, name1_id, name1_id,
-                                Variadic::String(value1_id));
-  context_.args_tracker->AddArg(TableId::kRawEvents, row, name2_id, name2_id,
-                                Variadic::Integer(kValue2));
+
+  context_.args_tracker->AddArgsTo(id)
+      .AddArg(name1_id, Variadic::String(value1_id))
+      .AddArg(name2_id, Variadic::Integer(kValue2));
   context_.args_tracker->Flush();
 
   auto metadata_filter = [](const char* metadata_name) {
@@ -1350,13 +1513,12 @@
   const int64_t kTimestamp1 = 10000000;
   const int64_t kTimestamp2 = 20000000;
   const int64_t kDuration = 10000;
-  const int64_t kThreadID = 100;
+  const uint32_t kThreadID = 100;
   const char* kCategory = "cat";
   const char* kName = "name";
 
-  UniqueTid utid = context_.storage->AddEmptyThread(kThreadID);
-  TrackId track =
-      context_.track_tracker->GetOrCreateDescriptorTrackForThread(utid);
+  UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
+  TrackId track = context_.track_tracker->InternThreadTrack(utid);
   context_.args_tracker->Flush();  // Flush track args.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
@@ -1379,13 +1541,13 @@
   EXPECT_EQ(result[0]["ph"].asString(), "X");
   EXPECT_EQ(result[0]["ts"].asInt64(), kTimestamp1 / 1000);
   EXPECT_EQ(result[0]["dur"].asInt64(), kDuration / 1000);
-  EXPECT_EQ(result[0]["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(result[0]["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(result[0]["cat"].asString(), kCategory);
   EXPECT_EQ(result[0]["name"].asString(), kName);
   EXPECT_EQ(result[1]["ph"].asString(), "X");
   EXPECT_EQ(result[1]["ts"].asInt64(), kTimestamp2 / 1000);
   EXPECT_EQ(result[1]["dur"].asInt64(), kDuration / 1000);
-  EXPECT_EQ(result[1]["tid"].asUInt(), kThreadID);
+  EXPECT_EQ(result[1]["tid"].asInt(), static_cast<int>(kThreadID));
   EXPECT_EQ(result[1]["cat"].asString(), kCategory);
   EXPECT_EQ(result[1]["name"].asString(), kName);
 }
diff --git a/src/trace_processor/filtered_row_index.cc b/src/trace_processor/filtered_row_index.cc
index 48c5d3c..376c021 100644
--- a/src/trace_processor/filtered_row_index.cc
+++ b/src/trace_processor/filtered_row_index.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/filtered_row_index.h"
 
+#include <iterator>
 #include <stddef.h>
 #include <numeric>
 
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index b28b89b..89798f2 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -39,9 +39,18 @@
 namespace trace_processor {
 namespace {
 
+#if !PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+const char kNoZlibErr[] =
+    "Cannot open compressed trace. zlib not enabled in the build config";
+#endif
+
+inline bool isspace(unsigned char c) {
+  return ::isspace(c);
+}
+
 std::string RemoveWhitespace(const std::string& input) {
   std::string str(input);
-  str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());
+  str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
   return str;
 }
 
@@ -115,12 +124,20 @@
         }
       case kGzipTraceType:
         PERFETTO_DLOG("gzip trace detected");
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
         reader_.reset(new GzipTraceParser(context_));
         break;
+#else
+        return util::ErrStatus(kNoZlibErr);
+#endif
       case kCtraceTraceType:
         PERFETTO_DLOG("ctrace trace detected");
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
         reader_.reset(new GzipTraceParser(context_));
         break;
+#else
+        return util::ErrStatus(kNoZlibErr);
+#endif
       case kUnknownTraceType:
         return util::ErrStatus("Unknown trace type provided");
     }
diff --git a/test/task_runner_thread_delegates.cc b/src/trace_processor/global_args_tracker.cc
similarity index 75%
rename from test/task_runner_thread_delegates.cc
rename to src/trace_processor/global_args_tracker.cc
index 291482f..9966108 100644
--- a/test/task_runner_thread_delegates.cc
+++ b/src/trace_processor/global_args_tracker.cc
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include "test/task_runner_thread_delegates.h"
+#include "src/trace_processor/global_args_tracker.h"
 
 namespace perfetto {
+namespace trace_processor {
 
-ServiceDelegate::~ServiceDelegate() = default;
-ProbesProducerDelegate::~ProbesProducerDelegate() = default;
-FakeProducerDelegate::~FakeProducerDelegate() = default;
+GlobalArgsTracker::GlobalArgsTracker(TraceProcessorContext* context)
+    : context_(context) {}
 
+}  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/global_args_tracker.h b/src/trace_processor/global_args_tracker.h
new file mode 100644
index 0000000..74c5152
--- /dev/null
+++ b/src/trace_processor/global_args_tracker.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 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_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
+
+#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/types/variadic.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Interns args into the storage from all ArgsTrackers across trace processor.
+// Note: most users will want to use ArgsTracker to push args to the strorage
+// and not this class. This class is really intended for ArgsTracker to use for
+// that purpose.
+class GlobalArgsTracker {
+ public:
+  struct Arg {
+    StringId flat_key = kNullStringId;
+    StringId key = kNullStringId;
+    Variadic value = Variadic::Integer(0);
+
+    Column* column;
+    uint32_t row;
+  };
+
+  struct ArgHasher {
+    uint64_t operator()(const Arg& arg) const noexcept {
+      base::Hash hash;
+      hash.Update(arg.key);
+      // We don't hash arg.flat_key because it's a subsequence of arg.key.
+      switch (arg.value.type) {
+        case Variadic::Type::kInt:
+          hash.Update(arg.value.int_value);
+          break;
+        case Variadic::Type::kUint:
+          hash.Update(arg.value.uint_value);
+          break;
+        case Variadic::Type::kString:
+          hash.Update(arg.value.string_value);
+          break;
+        case Variadic::Type::kReal:
+          hash.Update(arg.value.real_value);
+          break;
+        case Variadic::Type::kPointer:
+          hash.Update(arg.value.pointer_value);
+          break;
+        case Variadic::Type::kBool:
+          hash.Update(arg.value.bool_value);
+          break;
+        case Variadic::Type::kJson:
+          hash.Update(arg.value.json_value);
+          break;
+      }
+      return hash.digest();
+    }
+  };
+
+  GlobalArgsTracker(TraceProcessorContext* context);
+
+  ArgSetId AddArgSet(const std::vector<Arg>& args,
+                     uint32_t begin,
+                     uint32_t end) {
+    base::Hash hash;
+    for (uint32_t i = begin; i < end; i++) {
+      hash.Update(ArgHasher()(args[i]));
+    }
+
+    auto* arg_table = context_->storage->mutable_arg_table();
+
+    ArgSetHash digest = hash.digest();
+    auto it = arg_row_for_hash_.find(digest);
+    if (it != arg_row_for_hash_.end())
+      return arg_table->arg_set_id()[it->second];
+
+    // The +1 ensures that nothing has an id == kInvalidArgSetId == 0.
+    ArgSetId id = static_cast<uint32_t>(arg_row_for_hash_.size()) + 1;
+    arg_row_for_hash_.emplace(digest, arg_table->row_count());
+    for (uint32_t i = begin; i < end; i++) {
+      const auto& arg = args[i];
+
+      tables::ArgTable::Row row;
+      row.arg_set_id = id;
+      row.flat_key = arg.flat_key;
+      row.key = arg.key;
+      switch (arg.value.type) {
+        case Variadic::Type::kInt:
+          row.int_value = arg.value.int_value;
+          break;
+        case Variadic::Type::kUint:
+          row.int_value = static_cast<int64_t>(arg.value.uint_value);
+          break;
+        case Variadic::Type::kString:
+          row.string_value = arg.value.string_value;
+          break;
+        case Variadic::Type::kReal:
+          row.real_value = arg.value.real_value;
+          break;
+        case Variadic::Type::kPointer:
+          row.int_value = static_cast<int64_t>(arg.value.pointer_value);
+          break;
+        case Variadic::Type::kBool:
+          row.int_value = arg.value.bool_value;
+          break;
+        case Variadic::Type::kJson:
+          row.string_value = arg.value.json_value;
+          break;
+      }
+      row.value_type = context_->storage->GetIdForVariadicType(arg.value.type);
+      arg_table->Insert(row);
+    }
+    return id;
+  }
+
+ private:
+  using ArgSetHash = uint64_t;
+
+  std::unordered_map<ArgSetHash, uint32_t> arg_row_for_hash_;
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
diff --git a/src/trace_processor/heap_profile_tracker.cc b/src/trace_processor/heap_profile_tracker.cc
index a666dad..67883fc 100644
--- a/src/trace_processor/heap_profile_tracker.cc
+++ b/src/trace_processor/heap_profile_tracker.cc
@@ -24,6 +24,201 @@
 namespace perfetto {
 namespace trace_processor {
 
+namespace {
+struct MergedCallsite {
+  StringId frame_name;
+  StringId mapping_name;
+  base::Optional<uint32_t> parent_idx;
+  bool operator<(const MergedCallsite& o) const {
+    return std::tie(frame_name, mapping_name, parent_idx) <
+           std::tie(o.frame_name, o.mapping_name, o.parent_idx);
+  }
+};
+
+std::vector<MergedCallsite> GetMergedCallsites(TraceStorage* storage,
+                                               uint32_t callstack_row) {
+  const tables::StackProfileCallsiteTable& callsites_tbl =
+      storage->stack_profile_callsite_table();
+  const tables::StackProfileFrameTable& frames_tbl =
+      storage->stack_profile_frame_table();
+  const tables::SymbolTable& symbols_tbl = storage->symbol_table();
+  const tables::StackProfileMappingTable& mapping_tbl =
+      storage->stack_profile_mapping_table();
+
+  // TODO(fmayer): Clean up types and remove the static_cast.
+  uint32_t frame_idx = *frames_tbl.id().IndexOf(
+      FrameId(static_cast<uint32_t>(callsites_tbl.frame_id()[callstack_row])));
+
+  // TODO(fmayer): Clean up types and remove the static_cast.
+  uint32_t mapping_idx = *mapping_tbl.id().IndexOf(
+      MappingId(static_cast<uint32_t>(frames_tbl.mapping()[frame_idx])));
+  StringId mapping_name = mapping_tbl.name()[mapping_idx];
+
+  base::Optional<uint32_t> symbol_set_id =
+      frames_tbl.symbol_set_id()[frame_idx];
+
+  if (!symbol_set_id) {
+    StringId frame_name = frames_tbl.name()[frame_idx];
+    return {{frame_name, mapping_name, base::nullopt}};
+  }
+
+  std::vector<MergedCallsite> result;
+  // id == symbol_set_id for the bottommost frame.
+  // TODO(lalitm): Encode this optimization in the table and remove this
+  // custom optimization.
+  uint32_t symbol_set_idx = *symbols_tbl.id().IndexOf(SymbolId(*symbol_set_id));
+  for (uint32_t i = symbol_set_idx;
+       i < symbols_tbl.row_count() &&
+       symbols_tbl.symbol_set_id()[i] == *symbol_set_id;
+       ++i) {
+    result.emplace_back(
+        MergedCallsite{symbols_tbl.name()[i], mapping_name, base::nullopt});
+  }
+  return result;
+}
+}  // namespace
+
+std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> BuildNativeFlamegraph(
+    TraceStorage* storage,
+    UniquePid upid,
+    int64_t timestamp) {
+  const tables::HeapProfileAllocationTable& allocation_tbl =
+      storage->heap_profile_allocation_table();
+  const tables::StackProfileCallsiteTable& callsites_tbl =
+      storage->stack_profile_callsite_table();
+
+  StringId profile_type = storage->InternString("native");
+
+  std::vector<uint32_t> callsite_to_merged_callsite(callsites_tbl.row_count(),
+                                                    0);
+  std::map<MergedCallsite, uint32_t> merged_callsites_to_table_idx;
+
+  std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> tbl(
+      new tables::ExperimentalFlamegraphNodesTable(
+          storage->mutable_string_pool(), nullptr));
+
+  // FORWARD PASS:
+  // Aggregate callstacks by frame name / mapping name. Use symbolization data.
+  for (uint32_t i = 0; i < callsites_tbl.row_count(); ++i) {
+    base::Optional<uint32_t> parent_idx;
+    // TODO(fmayer): Clean up types and remove the conditional and static_cast.
+    if (callsites_tbl.parent_id()[i] != -1) {
+      auto parent_id = static_cast<uint32_t>(callsites_tbl.parent_id()[i]);
+      parent_idx = callsites_tbl.id().IndexOf(CallsiteId(parent_id));
+      parent_idx = callsite_to_merged_callsite[*parent_idx];
+      PERFETTO_CHECK(*parent_idx < i);
+    }
+
+    auto callsites = GetMergedCallsites(storage, i);
+    for (MergedCallsite& merged_callsite : callsites) {
+      merged_callsite.parent_idx = parent_idx;
+      auto it = merged_callsites_to_table_idx.find(merged_callsite);
+      if (it == merged_callsites_to_table_idx.end()) {
+        std::tie(it, std::ignore) = merged_callsites_to_table_idx.emplace(
+            merged_callsite, merged_callsites_to_table_idx.size());
+        tables::ExperimentalFlamegraphNodesTable::Row row{};
+        if (parent_idx) {
+          row.depth = tbl->depth()[*parent_idx] + 1;
+        } else {
+          row.depth = 0;
+        }
+        row.ts = timestamp;
+        row.upid = upid;
+        row.profile_type = profile_type;
+        row.name = merged_callsite.frame_name;
+        row.map_name = merged_callsite.mapping_name;
+        if (parent_idx)
+          row.parent_id = tbl->id()[*parent_idx].value;
+
+        parent_idx = *tbl->id().IndexOf(tbl->Insert(std::move(row)));
+        PERFETTO_CHECK(merged_callsites_to_table_idx.size() ==
+                       tbl->row_count());
+      }
+      parent_idx = it->second;
+    }
+    PERFETTO_CHECK(parent_idx);
+    callsite_to_merged_callsite[i] = *parent_idx;
+  }
+
+  // PASS OVER ALLOCATIONS:
+  // Aggregate allocations into the newly built tree.
+  auto filtered = allocation_tbl.Filter(
+      {allocation_tbl.ts().eq(timestamp), allocation_tbl.upid().eq(upid)});
+
+  if (filtered.row_count() == 0) {
+    return nullptr;
+  }
+
+  for (auto it = filtered.IterateRows(); it; it.Next()) {
+    int64_t size =
+        it.Get(static_cast<uint32_t>(
+                   tables::HeapProfileAllocationTable::ColumnIndex::size))
+            .long_value;
+    int64_t count =
+        it.Get(static_cast<uint32_t>(
+                   tables::HeapProfileAllocationTable::ColumnIndex::count))
+            .long_value;
+    int64_t callsite_id =
+        it
+            .Get(static_cast<uint32_t>(
+                tables::HeapProfileAllocationTable::ColumnIndex::callsite_id))
+            .long_value;
+
+    PERFETTO_CHECK((size <= 0 && count <= 0) || (size >= 0 && count >= 0));
+    uint32_t merged_idx =
+        callsite_to_merged_callsite[*callsites_tbl.id().IndexOf(
+            CallsiteId(static_cast<uint32_t>(callsite_id)))];
+    if (count > 0) {
+      // TODO(fmayer): Clean up types and remove the static_cast.
+      tbl->mutable_alloc_size()->Set(merged_idx,
+                                     tbl->alloc_size()[merged_idx] + size);
+      tbl->mutable_alloc_count()->Set(merged_idx,
+                                      tbl->alloc_count()[merged_idx] + count);
+    }
+
+    // TODO(fmayer): Clean up types and remove the static_cast.
+    tbl->mutable_size()->Set(merged_idx, tbl->size()[merged_idx] + size);
+    tbl->mutable_count()->Set(merged_idx, tbl->count()[merged_idx] + count);
+  }
+
+  // BACKWARD PASS:
+  // Propagate sizes to parents.
+  for (int64_t i = tbl->row_count() - 1; i >= 0; --i) {
+    uint32_t idx = static_cast<uint32_t>(i);
+
+    tbl->mutable_cumulative_size()->Set(
+        idx, tbl->cumulative_size()[idx] + tbl->size()[idx]);
+    tbl->mutable_cumulative_count()->Set(
+        idx, tbl->cumulative_count()[idx] + tbl->count()[idx]);
+
+    tbl->mutable_cumulative_alloc_size()->Set(
+        idx, tbl->cumulative_alloc_size()[idx] + tbl->alloc_size()[idx]);
+    tbl->mutable_cumulative_alloc_count()->Set(
+        idx, tbl->cumulative_alloc_count()[idx] + tbl->alloc_count()[idx]);
+
+    auto parent = tbl->parent_id()[idx];
+    if (parent) {
+      uint32_t parent_idx = *tbl->id().IndexOf(
+          tables::ExperimentalFlamegraphNodesTable::Id(*parent));
+      tbl->mutable_cumulative_size()->Set(
+          parent_idx,
+          tbl->cumulative_size()[parent_idx] + tbl->cumulative_size()[idx]);
+      tbl->mutable_cumulative_count()->Set(
+          parent_idx,
+          tbl->cumulative_count()[parent_idx] + tbl->cumulative_count()[idx]);
+
+      tbl->mutable_cumulative_alloc_size()->Set(
+          parent_idx, tbl->cumulative_alloc_size()[parent_idx] +
+                          tbl->cumulative_alloc_size()[idx]);
+      tbl->mutable_cumulative_alloc_count()->Set(
+          parent_idx, tbl->cumulative_alloc_count()[parent_idx] +
+                          tbl->cumulative_alloc_count()[idx]);
+    }
+  }
+
+  return tbl;
+}
+
 HeapProfileTracker::HeapProfileTracker(TraceProcessorContext* context)
     : context_(context), empty_(context_->storage->InternString({"", 0})) {}
 
@@ -32,11 +227,27 @@
 void HeapProfileTracker::SetProfilePacketIndex(uint32_t seq_id,
                                                uint64_t index) {
   SequenceState& sequence_state = sequence_state_[seq_id];
-  if (sequence_state.last_profile_packet_index != 0 &&
-      sequence_state.last_profile_packet_index + 1 != index) {
+  bool dropped_packet = false;
+  // heapprofd starts counting at index = 0.
+  if (!sequence_state.prev_index && index != 0) {
+    dropped_packet = true;
+  }
+
+  if (sequence_state.prev_index && *sequence_state.prev_index + 1 != index) {
+    dropped_packet = true;
+  }
+
+  if (dropped_packet) {
+    if (sequence_state.prev_index) {
+      PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
+                    *sequence_state.prev_index, index);
+    } else {
+      PERFETTO_ELOG("Invalid first packet index %" PRIu64 " (!= 0)", index);
+    }
+
     context_->storage->IncrementStats(stats::heapprofd_missing_packet);
   }
-  sequence_state.last_profile_packet_index = index;
+  sequence_state.prev_index = index;
 }
 
 void HeapProfileTracker::AddAllocation(
@@ -45,29 +256,27 @@
     const SourceAllocation& alloc,
     const StackProfileTracker::InternLookup* intern_lookup) {
   SequenceState& sequence_state = sequence_state_[seq_id];
-  auto maybe_callstack_id =
-      stack_profile_tracker->FindCallstack(alloc.callstack_id, intern_lookup);
+
+  auto maybe_callstack_id = stack_profile_tracker->FindOrInsertCallstack(
+      alloc.callstack_id, intern_lookup);
   if (!maybe_callstack_id)
     return;
 
-  int64_t callstack_id = *maybe_callstack_id;
+  CallsiteId callstack_id = *maybe_callstack_id;
 
   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
       static_cast<uint32_t>(alloc.pid));
 
   tables::HeapProfileAllocationTable::Row alloc_row{
-      alloc.timestamp, upid, callstack_id,
+      alloc.timestamp, upid, callstack_id.value,
       static_cast<int64_t>(alloc.alloc_count),
       static_cast<int64_t>(alloc.self_allocated)};
 
   tables::HeapProfileAllocationTable::Row free_row{
-      alloc.timestamp, upid, callstack_id,
+      alloc.timestamp, upid, callstack_id.value,
       -static_cast<int64_t>(alloc.free_count),
       -static_cast<int64_t>(alloc.self_freed)};
 
-  tables::HeapProfileAllocationTable::Row alloc_delta = alloc_row;
-  tables::HeapProfileAllocationTable::Row free_delta = free_row;
-
   auto prev_alloc_it = sequence_state.prev_alloc.find({upid, callstack_id});
   if (prev_alloc_it == sequence_state.prev_alloc.end()) {
     std::tie(prev_alloc_it, std::ignore) = sequence_state.prev_alloc.emplace(
@@ -76,8 +285,6 @@
   }
 
   tables::HeapProfileAllocationTable::Row& prev_alloc = prev_alloc_it->second;
-  alloc_delta.count -= prev_alloc.count;
-  alloc_delta.size -= prev_alloc.size;
 
   auto prev_free_it = sequence_state.prev_free.find({upid, callstack_id});
   if (prev_free_it == sequence_state.prev_free.end()) {
@@ -87,15 +294,59 @@
   }
 
   tables::HeapProfileAllocationTable::Row& prev_free = prev_free_it->second;
+
+  std::set<CallsiteId>& callstacks_for_source_callstack_id =
+      sequence_state.seen_callstacks[std::make_pair(upid, alloc.callstack_id)];
+  bool new_callstack;
+  std::tie(std::ignore, new_callstack) =
+      callstacks_for_source_callstack_id.emplace(callstack_id);
+
+  if (new_callstack) {
+    sequence_state.alloc_correction[alloc.callstack_id] = prev_alloc;
+    sequence_state.free_correction[alloc.callstack_id] = prev_free;
+  }
+
+  auto alloc_correction_it =
+      sequence_state.alloc_correction.find(alloc.callstack_id);
+  if (alloc_correction_it != sequence_state.alloc_correction.end()) {
+    const auto& alloc_correction = alloc_correction_it->second;
+    alloc_row.count += alloc_correction.count;
+    alloc_row.size += alloc_correction.size;
+  }
+
+  auto free_correction_it =
+      sequence_state.free_correction.find(alloc.callstack_id);
+  if (free_correction_it != sequence_state.free_correction.end()) {
+    const auto& free_correction = free_correction_it->second;
+    free_row.count += free_correction.count;
+    free_row.size += free_correction.size;
+  }
+
+  tables::HeapProfileAllocationTable::Row alloc_delta = alloc_row;
+  tables::HeapProfileAllocationTable::Row free_delta = free_row;
+
+  alloc_delta.count -= prev_alloc.count;
+  alloc_delta.size -= prev_alloc.size;
+
   free_delta.count -= prev_free.count;
   free_delta.size -= prev_free.size;
 
-  if (alloc_delta.count)
+  if (alloc_delta.count < 0 || alloc_delta.size < 0 || free_delta.count > 0 ||
+      free_delta.size > 0) {
+    PERFETTO_DLOG("Non-monotonous allocation.");
+    context_->storage->IncrementIndexedStats(stats::heapprofd_malformed_packet,
+                                             static_cast<int>(upid));
+    return;
+  }
+
+  if (alloc_delta.count) {
     context_->storage->mutable_heap_profile_allocation_table()->Insert(
         alloc_delta);
-  if (free_delta.count)
+  }
+  if (free_delta.count) {
     context_->storage->mutable_heap_profile_allocation_table()->Insert(
         free_delta);
+  }
 
   prev_alloc = alloc_row;
   prev_free = free_row;
diff --git a/src/trace_processor/heap_profile_tracker.h b/src/trace_processor/heap_profile_tracker.h
index 1d121aa..fe40a84 100644
--- a/src/trace_processor/heap_profile_tracker.h
+++ b/src/trace_processor/heap_profile_tracker.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
 
 #include <deque>
+#include <set>
 #include <unordered_map>
 
 #include "perfetto/ext/base/optional.h"
@@ -30,6 +31,11 @@
 namespace perfetto {
 namespace trace_processor {
 
+std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> BuildNativeFlamegraph(
+    TraceStorage* storage,
+    UniquePid upid,
+    int64_t timestamp);
+
 class TraceProcessorContext;
 
 class HeapProfileTracker {
@@ -77,14 +83,34 @@
   struct SequenceState {
     std::vector<SourceAllocation> pending_allocs;
 
-    std::unordered_map<std::pair<UniquePid, int64_t>,
+    std::unordered_map<std::pair<UniquePid, CallsiteId>,
                        tables::HeapProfileAllocationTable::Row>
         prev_alloc;
-    std::unordered_map<std::pair<UniquePid, int64_t>,
+    std::unordered_map<std::pair<UniquePid, CallsiteId>,
                        tables::HeapProfileAllocationTable::Row>
         prev_free;
 
-    uint64_t last_profile_packet_index = 0;
+    // For continuous dumps, we only store the delta in the data-base. To do
+    // this, we subtract the previous dump's value. Sometimes, we should not
+    // do that subtraction, because heapprofd garbage collects stacks that
+    // have no unfreed allocations. If the application then allocations again
+    // at that stack, it gets recreated and initialized to zero.
+    //
+    // To correct for this, we add the previous' stacks value to the current
+    // one, and then handle it as normal. If it is the first time we see a
+    // SourceCallstackId for a CallsiteId, we put the previous value into
+    // the correction maps below.
+    std::map<std::pair<UniquePid, StackProfileTracker::SourceCallstackId>,
+             std::set<CallsiteId>>
+        seen_callstacks;
+    std::map<StackProfileTracker::SourceCallstackId,
+             tables::HeapProfileAllocationTable::Row>
+        alloc_correction;
+    std::map<StackProfileTracker::SourceCallstackId,
+             tables::HeapProfileAllocationTable::Row>
+        free_correction;
+
+    base::Optional<uint64_t> prev_index;
   };
   std::map<uint32_t, SequenceState> sequence_state_;
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/heap_profile_tracker_unittest.cc b/src/trace_processor/heap_profile_tracker_unittest.cc
index 91c3f93..d9d3e1d 100644
--- a/src/trace_processor/heap_profile_tracker_unittest.cc
+++ b/src/trace_processor/heap_profile_tracker_unittest.cc
@@ -181,11 +181,11 @@
 int64_t FindCallstack(const TraceStorage& storage,
                       int64_t depth,
                       int64_t parent,
-                      int64_t frame_id) {
+                      FrameId frame_id) {
   const auto& callsites = storage.stack_profile_callsite_table();
   for (uint32_t i = 0; i < callsites.row_count(); ++i) {
     if (callsites.depth()[i] == depth && callsites.parent_id()[i] == parent &&
-        callsites.frame_id()[i] == frame_id) {
+        callsites.frame_id()[i] == frame_id.value) {
       return static_cast<int64_t>(i);
     }
   }
@@ -327,7 +327,6 @@
     const StackProfileTracker::SourceCallstack& callstack = callstacks[i];
     for (size_t depth = 0; depth < callstack.size(); ++depth) {
       auto frame_id = spt->GetDatabaseFrameIdForTesting(callstack[depth]);
-      ASSERT_NE(frame_id, -1);
       int64_t self = FindCallstack(
           *context.storage, static_cast<int64_t>(depth), parent, frame_id);
       ASSERT_NE(self, -1);
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index d61c63c..ac3d374 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -74,7 +74,8 @@
       signal_deliver_id_(context->storage->InternString("signal_deliver")),
       oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
       lmk_id_(context->storage->InternString("mem.lmk")),
-      comm_name_id_(context->storage->InternString("comm")) {
+      comm_name_id_(context->storage->InternString("comm")),
+      signal_name_id_(context_->storage->InternString("signal.sig")) {
   // Build the lookup table for the strings inside ftrace events (e.g. the
   // name of ftrace event fields and the names of their args).
   for (size_t i = 0; i < GetDescriptorsSize(); i++) {
@@ -357,25 +358,22 @@
   protos::pbzero::GenericFtraceEvent::Decoder evt(blob.data, blob.size);
   StringId event_id = context_->storage->InternString(evt.event_name());
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid);
-  uint32_t row = context_->storage->mutable_raw_events()->AddRawEvent(
-      ts, event_id, cpu, utid);
+  RawId id =
+      context_->storage->mutable_raw_table()->Insert({ts, event_id, cpu, utid});
+  auto inserter = context_->args_tracker->AddArgsTo(id);
 
   for (auto it = evt.field(); it; ++it) {
     protos::pbzero::GenericFtraceEvent::Field::Decoder fld(*it);
     auto field_name_id = context_->storage->InternString(fld.name());
     if (fld.has_int_value()) {
-      context_->args_tracker->AddArg(TableId::kRawEvents, row, field_name_id,
-                                     field_name_id,
-                                     Variadic::Integer(fld.int_value()));
+      inserter.AddArg(field_name_id, Variadic::Integer(fld.int_value()));
     } else if (fld.has_uint_value()) {
-      context_->args_tracker->AddArg(
-          TableId::kRawEvents, row, field_name_id, field_name_id,
+      inserter.AddArg(
+          field_name_id,
           Variadic::Integer(static_cast<int64_t>(fld.uint_value())));
     } else if (fld.has_str_value()) {
       StringId str_value = context_->storage->InternString(fld.str_value());
-      context_->args_tracker->AddArg(TableId::kRawEvents, row, field_name_id,
-                                     field_name_id,
-                                     Variadic::String(str_value));
+      inserter.AddArg(field_name_id, Variadic::String(str_value));
     }
   }
 }
@@ -385,6 +383,9 @@
                                          uint32_t cpu,
                                          uint32_t tid,
                                          ConstBytes blob) {
+  if (PERFETTO_UNLIKELY(!context_->config.ingest_ftrace_in_raw_table))
+    return;
+
   ProtoDecoder decoder(blob.data, blob.size);
   if (ftrace_id >= GetDescriptorsSize()) {
     PERFETTO_DLOG("Event with id: %d does not exist and cannot be parsed.",
@@ -395,8 +396,10 @@
   MessageDescriptor* m = GetMessageDescriptorForId(ftrace_id);
   const auto& message_strings = ftrace_message_strings_[ftrace_id];
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid);
-  uint32_t raw_event_row = context_->storage->mutable_raw_events()->AddRawEvent(
-      ts, message_strings.message_name_id, cpu, utid);
+  RawId id = context_->storage->mutable_raw_table()->Insert(
+      {ts, message_strings.message_name_id, cpu, utid});
+  auto inserter = context_->args_tracker->AddArgsTo(id);
+
   for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) {
     if (PERFETTO_UNLIKELY(fld.id() >= kMaxFtraceEventFields)) {
       PERFETTO_DLOG(
@@ -415,9 +418,7 @@
       case ProtoSchemaType::kSint64:
       case ProtoSchemaType::kBool:
       case ProtoSchemaType::kEnum: {
-        context_->args_tracker->AddArg(TableId::kRawEvents, raw_event_row,
-                                       name_id, name_id,
-                                       Variadic::Integer(fld.as_int64()));
+        inserter.AddArg(name_id, Variadic::Integer(fld.as_int64()));
         break;
       }
       case ProtoSchemaType::kUint32:
@@ -427,29 +428,22 @@
         // Note that SQLite functions will still treat unsigned values
         // as a signed 64 bit integers (but the translation back to ftrace
         // refers to this storage directly).
-        context_->args_tracker->AddArg(
-            TableId::kRawEvents, raw_event_row, name_id, name_id,
-            Variadic::UnsignedInteger(fld.as_uint64()));
+        inserter.AddArg(name_id, Variadic::UnsignedInteger(fld.as_uint64()));
         break;
       }
       case ProtoSchemaType::kString:
       case ProtoSchemaType::kBytes: {
         StringId value = context_->storage->InternString(fld.as_string());
-        context_->args_tracker->AddArg(TableId::kRawEvents, raw_event_row,
-                                       name_id, name_id,
-                                       Variadic::String(value));
+        inserter.AddArg(name_id, Variadic::String(value));
         break;
       }
       case ProtoSchemaType::kDouble: {
-        context_->args_tracker->AddArg(TableId::kRawEvents, raw_event_row,
-                                       name_id, name_id,
-                                       Variadic::Real(fld.as_double()));
+        inserter.AddArg(name_id, Variadic::Real(fld.as_double()));
         break;
       }
       case ProtoSchemaType::kFloat: {
-        context_->args_tracker->AddArg(
-            TableId::kRawEvents, raw_event_row, name_id, name_id,
-            Variadic::Real(static_cast<double>(fld.as_float())));
+        inserter.AddArg(name_id,
+                        Variadic::Real(static_cast<double>(fld.as_float())));
         break;
       }
       case ProtoSchemaType::kUnknown:
@@ -614,11 +608,9 @@
       static_cast<uint32_t>(sig.pid()));
   InstantId id = context_->event_tracker->PushInstant(ts, signal_generate_id_,
                                                       utid, RefType::kRefUtid);
-  uint32_t row = *context_->storage->instant_table().id().IndexOf(id);
 
-  StringId key = context_->storage->InternString("signal.sig");
-  context_->args_tracker->AddArg(TableId::kInstants, row, key, key,
-                                 Variadic::Integer(sig.sig()));
+  context_->args_tracker->AddArgsTo(id).AddArg(signal_name_id_,
+                                               Variadic::Integer(sig.sig()));
 }
 
 void FtraceParser::ParseSignalDeliver(int64_t ts,
@@ -628,11 +620,9 @@
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
   InstantId id = context_->event_tracker->PushInstant(ts, signal_deliver_id_,
                                                       utid, RefType::kRefUtid);
-  uint32_t row = *context_->storage->instant_table().id().IndexOf(id);
 
-  StringId key = context_->storage->InternString("signal.sig");
-  context_->args_tracker->AddArg(TableId::kInstants, row, key, key,
-                                 Variadic::Integer(sig.sig()));
+  context_->args_tracker->AddArgsTo(id).AddArg(signal_name_id_,
+                                               Variadic::Integer(sig.sig()));
 }
 
 void FtraceParser::ParseLowmemoryKill(int64_t ts, ConstBytes blob) {
@@ -651,13 +641,12 @@
 
   InstantId id = context_->event_tracker->PushInstant(
       ts, lmk_id_, opt_utid.value(), RefType::kRefUtid, true);
-  uint32_t row = *context_->storage->instant_table().id().IndexOf(id);
 
   // Store the comm as an arg.
   auto comm_id = context_->storage->InternString(
       lmk.has_comm() ? lmk.comm() : base::StringView());
-  context_->args_tracker->AddArg(TableId::kInstants, row, comm_name_id_,
-                                 comm_name_id_, Variadic::String(comm_id));
+  context_->args_tracker->AddArgsTo(id).AddArg(comm_name_id_,
+                                               Variadic::String(comm_id));
 }
 
 void FtraceParser::ParseOOMScoreAdjUpdate(int64_t ts, ConstBytes blob) {
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index bd8ff81..10eacfe 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -94,10 +94,11 @@
   const StringId oom_score_adj_id_;
   const StringId lmk_id_;
   const StringId comm_name_id_;
+  const StringId signal_name_id_;
 
   struct FtraceMessageStrings {
     // The string id of name of the event field (e.g. sched_switch's id).
-    StringId message_name_id = 0;
+    StringId message_name_id = kNullStringId;
     std::array<StringId, kMaxFtraceEventFields> field_name_ids;
   };
   std::vector<FtraceMessageStrings> ftrace_message_strings_;
@@ -107,9 +108,9 @@
     MmEventCounterNames(StringId _count, StringId _max_lat, StringId _avg_lat)
         : count(_count), max_lat(_max_lat), avg_lat(_avg_lat) {}
 
-    StringId count = 0;
-    StringId max_lat = 0;
-    StringId avg_lat = 0;
+    StringId count = kNullStringId;
+    StringId max_lat = kNullStringId;
+    StringId avg_lat = kNullStringId;
   };
 
   // Keep kMmEventCounterSize equal to mm_event_type::MM_TYPE_NUM in the kernel.
diff --git a/src/trace_processor/importers/ftrace/rss_stat_tracker.cc b/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
index db444a7..f536a02 100644
--- a/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
+++ b/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
@@ -69,24 +69,34 @@
 base::Optional<UniqueTid> RssStatTracker::FindUtidForMmId(int64_t mm_id,
                                                           bool is_curr,
                                                           uint32_t pid) {
-  auto it = mm_id_to_utid_.find(mm_id);
-  if (!is_curr) {
-    return it == mm_id_to_utid_.end() ? base::nullopt
-                                      : base::make_optional(it->second);
+  // If curr is true, we can just overwrite the state in the map and return
+  // the utid correspodning to |pid|.
+  if (is_curr) {
+    UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+    mm_id_to_utid_[mm_id] = utid;
+    return utid;
   }
 
+  // If curr is false, try and lookup the utid we previously saw for this
+  // mm id.
+  auto it = mm_id_to_utid_.find(mm_id);
+  if (it == mm_id_to_utid_.end())
+    return base::nullopt;
+
+  // If the utid in the map is the same as our current utid but curr is false,
+  // that means we are in the middle of a process changing mm structs (i.e. in
+  // the middle of a vfork + exec). Therefore, we should discard the association
+  // of this vm struct with this thread.
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
-  if (it != mm_id_to_utid_.end() && it->second != utid) {
-    // Since both of these structs have the same mm hash and both say that
-    // the mm hash is for the current project, we can assume they belong to
-    // the same process so we can associate them together.
-    // TODO(lalitm): investigate if it's possible for mm_id to be reused
-    // between different processes if we have pid reuse and get unlucky. If
-    // so, we'll need to do some more careful tracking here.
-    context_->process_tracker->AssociateThreads(it->second, utid);
+  if (it->second == utid) {
+    mm_id_to_utid_.erase(it);
+    return base::nullopt;
   }
-  mm_id_to_utid_[mm_id] = utid;
-  return utid;
+
+  // This case happens when a process is changing the VM of another process and
+  // we know that the utid corresponding to the target process. Just return that
+  // utid.
+  return it->second;
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index df5f575..62fcec4 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -26,7 +26,7 @@
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/stats.h"
 #include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/variadic.h"
 
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
@@ -81,7 +81,7 @@
   PERFETTO_DCHECK(cpu < kMaxCpus);
 
   StringId next_comm_id = context_->storage->InternString(next_comm);
-  auto next_utid =
+  UniqueTid next_utid =
       context_->process_tracker->UpdateThreadName(next_pid, next_comm_id);
 
   // First use this data to close the previous slice.
@@ -132,7 +132,7 @@
   context_->event_tracker->UpdateMaxTimestamp(ts);
   PERFETTO_DCHECK(cpu < kMaxCpus);
 
-  auto next_utid =
+  UniqueTid next_utid =
       context_->process_tracker->UpdateThreadName(next_pid, next_comm_id);
 
   auto* pending_sched = &pending_sched_per_cpu_[cpu];
@@ -167,8 +167,7 @@
 
   // Do a fresh task name lookup in case it was updated by a task_rename while
   // scheduled.
-  const auto& prev_thread = context_->storage->GetThread(prev_utid);
-  StringId prev_comm_id = prev_thread.name_id;
+  StringId prev_comm_id = context_->storage->thread_table().name()[prev_utid];
 
   auto new_slice_idx = AddRawEventAndStartSlice(
       cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio, prev_state,
@@ -193,29 +192,30 @@
                                                    uint32_t next_pid,
                                                    StringId next_comm_id,
                                                    int32_t next_prio) {
-  // Push the raw event - this is done as the raw ftrace event codepath does
-  // not insert sched_switch.
-  uint32_t row = context_->storage->mutable_raw_events()->AddRawEvent(
-      ts, sched_switch_id_, cpu, prev_utid);
+  if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
+    // Push the raw event - this is done as the raw ftrace event codepath does
+    // not insert sched_switch.
+    RawId id = context_->storage->mutable_raw_table()->Insert(
+        {ts, sched_switch_id_, cpu, prev_utid});
 
-  // Note: this ordering is important. The events should be pushed in the same
-  // order as the order of fields in the proto; this is used by the raw table to
-  // index these events using the field ids.
-  using SS = protos::pbzero::SchedSwitchFtraceEvent;
+    // Note: this ordering is important. The events should be pushed in the same
+    // order as the order of fields in the proto; this is used by the raw table
+    // to index these events using the field ids.
+    using SS = protos::pbzero::SchedSwitchFtraceEvent;
 
-  ArgsTracker::BoundInserter inserter(context_->args_tracker.get(),
-                                      TableId::kRawEvents, row);
-  auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
-    StringId key = sched_switch_field_ids_[static_cast<size_t>(field_num)];
-    inserter.AddArg(key, var);
-  };
-  add_raw_arg(SS::kPrevCommFieldNumber, Variadic::String(prev_comm_id));
-  add_raw_arg(SS::kPrevPidFieldNumber, Variadic::Integer(prev_pid));
-  add_raw_arg(SS::kPrevPrioFieldNumber, Variadic::Integer(prev_prio));
-  add_raw_arg(SS::kPrevStateFieldNumber, Variadic::Integer(prev_state));
-  add_raw_arg(SS::kNextCommFieldNumber, Variadic::String(next_comm_id));
-  add_raw_arg(SS::kNextPidFieldNumber, Variadic::Integer(next_pid));
-  add_raw_arg(SS::kNextPrioFieldNumber, Variadic::Integer(next_prio));
+    auto inserter = context_->args_tracker->AddArgsTo(id);
+    auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
+      StringId key = sched_switch_field_ids_[static_cast<size_t>(field_num)];
+      inserter.AddArg(key, var);
+    };
+    add_raw_arg(SS::kPrevCommFieldNumber, Variadic::String(prev_comm_id));
+    add_raw_arg(SS::kPrevPidFieldNumber, Variadic::Integer(prev_pid));
+    add_raw_arg(SS::kPrevPrioFieldNumber, Variadic::Integer(prev_prio));
+    add_raw_arg(SS::kPrevStateFieldNumber, Variadic::Integer(prev_state));
+    add_raw_arg(SS::kNextCommFieldNumber, Variadic::String(next_comm_id));
+    add_raw_arg(SS::kNextPidFieldNumber, Variadic::Integer(next_pid));
+    add_raw_arg(SS::kNextPrioFieldNumber, Variadic::Integer(next_prio));
+  }
 
   // Open a new scheduling slice, corresponding to the task that was
   // just switched to.
@@ -273,23 +273,27 @@
   }
   auto curr_utid = pending_sched->last_utid;
 
-  // Add an entry to the raw table.
-  uint32_t row = context_->storage->mutable_raw_events()->AddRawEvent(
-      ts, sched_waking_id_, cpu, curr_utid);
+  if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
+    // Add an entry to the raw table.
+    RawId id = context_->storage->mutable_raw_table()->Insert(
+        {ts, sched_waking_id_, cpu, curr_utid});
 
-  // "success" is hardcoded as always 1 by the kernel, with a TODO to remove it.
-  static constexpr int32_t kHardcodedSuccess = 1;
+    // "success" is hardcoded as always 1 by the kernel, with a TODO to remove
+    // it.
+    static constexpr int32_t kHardcodedSuccess = 1;
 
-  using SW = protos::pbzero::SchedWakingFtraceEvent;
-  auto add_raw_arg = [this, row](int field_num, Variadic var) {
-    StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
-    context_->args_tracker->AddArg(TableId::kRawEvents, row, key, key, var);
-  };
-  add_raw_arg(SW::kCommFieldNumber, Variadic::String(comm_id));
-  add_raw_arg(SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
-  add_raw_arg(SW::kPrioFieldNumber, Variadic::Integer(prio));
-  add_raw_arg(SW::kSuccessFieldNumber, Variadic::Integer(kHardcodedSuccess));
-  add_raw_arg(SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
+    using SW = protos::pbzero::SchedWakingFtraceEvent;
+    auto inserter = context_->args_tracker->AddArgsTo(id);
+    auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
+      StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
+      inserter.AddArg(key, var);
+    };
+    add_raw_arg(SW::kCommFieldNumber, Variadic::String(comm_id));
+    add_raw_arg(SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
+    add_raw_arg(SW::kPrioFieldNumber, Variadic::Integer(prio));
+    add_raw_arg(SW::kSuccessFieldNumber, Variadic::Integer(kHardcodedSuccess));
+    add_raw_arg(SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
+  }
 
   // Add a waking entry to the instants.
   auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
index 660c680..46f0815 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
@@ -253,11 +253,10 @@
                                   static_cast<uint32_t>(tinfo.pid));
           InstantId id = context_->event_tracker->PushInstant(
               ts, name, utid, RefType::kRefUtid);
-          uint32_t row = *context_->storage->instant_table().id().IndexOf(id);
+          auto inserter = context_->args_tracker->AddArgsTo(id);
           for (const Arg& arg : args) {
-            context_->args_tracker->AddArg(
-                TableId::kInstants, row, arg.name, arg.name,
-                arg.value.ToStorageVariadic(context_->storage.get()));
+            inserter.AddArg(
+                arg.name, arg.value.ToStorageVariadic(context_->storage.get()));
           }
           context_->args_tracker->Flush();
           break;
@@ -379,10 +378,10 @@
               name, correlation_id);
           InstantId id = context_->event_tracker->PushInstant(
               ts, name, track_id.value, RefType::kRefTrack);
-          uint32_t row = *context_->storage->instant_table().id().IndexOf(id);
+          auto inserter = context_->args_tracker->AddArgsTo(id);
           for (const Arg& arg : args) {
-            context_->args_tracker->AddArg(
-                TableId::kInstants, row, arg.name, arg.name,
+            inserter.AddArg(
+                arg.name, arg.name,
                 arg.value.ToStorageVariadic(context_->storage.get()));
           }
           context_->args_tracker->Flush();
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
index ac6b064..badef38 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
@@ -443,7 +443,7 @@
 
           UniqueTid utid = procs->UpdateThread(static_cast<uint32_t>(obj_id),
                                                static_cast<uint32_t>(pid));
-          storage->GetMutableThread(utid)->name_id = name;
+          storage->mutable_thread_table()->mutable_name()->Set(utid, name);
           break;
         }
         default: {
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 178be15..1952fef 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -229,11 +229,9 @@
     // arg_set_id when the arg tracker is flushed.
     auto id = context_->metadata_tracker->AppendMetadata(
         metadata::android_packages_list, Variadic::Integer(0));
-    uint32_t row = *context_->storage->metadata_table().id().IndexOf(id);
-    auto add_arg = [this, row](base::StringView name, Variadic value) {
+    auto add_arg = [this, id](base::StringView name, Variadic value) {
       StringId key_id = context_->storage->InternString(name);
-      context_->args_tracker->AddArg(TableId::kMetadataTable, row, key_id,
-                                     key_id, value);
+      context_->args_tracker->AddArgsTo(id).AddArg(key_id, value);
     };
     protos::pbzero::PackagesList_PackageInfo::Decoder pkg(*it);
     add_arg("name",
diff --git a/src/trace_processor/importers/proto/args_table_utils_unittest.cc b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
index 4cc03e9..df023b4 100644
--- a/src/trace_processor/importers/proto/args_table_utils_unittest.cc
+++ b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
@@ -44,24 +44,22 @@
   ArgsTableUtilsTest() {
     context_.storage.reset(new TraceStorage);
     storage_ = context_.storage.get();
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     sequence_state_.reset(new PacketSequenceState(&context_));
   }
 
   bool HasArg(ArgSetId set_id, const base::StringView& key, Variadic value) {
-    const auto& args = storage_->args();
+    const auto& args = storage_->arg_table();
     auto key_id = storage_->string_pool().GetId(key);
     EXPECT_TRUE(key_id);
-    auto rows =
-        std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
+
+    RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
     bool found = false;
-    for (; rows.first != rows.second; rows.first++) {
-      size_t index = static_cast<size_t>(
-          std::distance(args.set_ids().begin(), rows.first));
-      if (args.keys()[index] == key_id) {
-        EXPECT_EQ(args.flat_keys()[index], key_id);
-        if (args.flat_keys()[index] == key_id &&
-            args.arg_values()[index] == value) {
+    for (auto it = rm.IterateRows(); it; it.Next()) {
+      if (args.key()[it.row()] == key_id) {
+        EXPECT_EQ(args.flat_key()[it.row()], key_id);
+        if (storage_->GetArgValue(it.row()) == value) {
           found = true;
           break;
         }
@@ -132,8 +130,7 @@
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
                            << status.message();
 
-  ArgsTracker::BoundInserter inserter(context_.args_tracker.get(),
-                                      TableId::kTrack, 0);
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
       ".protozero.test.protos.EveryField", &inserter);
@@ -205,8 +202,7 @@
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
                            << status.message();
 
-  ArgsTracker::BoundInserter inserter(context_.args_tracker.get(),
-                                      TableId::kTrack, 0);
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
       ".protozero.test.protos.NestedA", &inserter);
@@ -234,8 +230,7 @@
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
                            << status.message();
 
-  ArgsTracker::BoundInserter inserter(context_.args_tracker.get(),
-                                      TableId::kTrack, 0);
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
       ".protozero.test.protos.CamelCaseFields", &inserter);
@@ -278,8 +273,7 @@
         return true;
       });
 
-  ArgsTracker::BoundInserter inserter(context_.args_tracker.get(),
-                                      TableId::kTrack, 0);
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
       ".protozero.test.protos.NestedA", &inserter);
@@ -319,8 +313,7 @@
         return false;
       });
 
-  ArgsTracker::BoundInserter inserter(context_.args_tracker.get(),
-                                      TableId::kTrack, 0);
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
       ".protozero.test.protos.NestedA", &inserter);
@@ -385,8 +378,7 @@
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
                            << status.message();
 
-  ArgsTracker::BoundInserter inserter(context_.args_tracker.get(),
-                                      TableId::kTrack, 0);
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
       ".protozero.test.protos.NestedA", &inserter);
diff --git a/src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h b/src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h
index f13eaa1..adc3f2d 100644
--- a/src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h
+++ b/src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h
@@ -25,17 +25,17 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// f242f1ac484bbe7ba4c45e77b56ab588f8015196
+// d6628b15181dba5287e35b56b966b39ea93d42b1
 // SHA1(protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto)
-// 360017d4658dfd81fbf16af8cc2abb994958af05
+// b7dab60e553ebda32178373fa290f99afb94e55a
 
 // This is the proto ChromeCompositorSchedulerState encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 10133> kChromeCompositorSchedulerStateDescriptor{
-    {0x0a, 0xd6, 0x01, 0x0a, 0x37, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+constexpr std::array<uint8_t, 10125> kChromeCompositorSchedulerStateDescriptor{
+    {0x0a, 0xd2, 0x01, 0x0a, 0x37, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61,
      0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65,
      0x6e, 0x74, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f,
@@ -52,834 +52,833 @@
      0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65,
      0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d,
      0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c,
-     0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x02, 0x48,
-     0x03, 0x0a, 0xb9, 0x4d, 0x0a, 0x49, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72,
-     0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76,
-     0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x63,
-     0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x63,
-     0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x1a, 0x37, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
+     0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0xb5, 0x4d,
+     0x0a, 0x49, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
      0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f,
      0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x0b,
-     0x0a, 0x1e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70,
-     0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75,
-     0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x52, 0x0a, 0x0d,
-     0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-     0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65,
+     0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f,
+     0x73, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75,
+     0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x37, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63,
+     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x73, 0x6f, 0x75, 0x72,
+     0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x0b, 0x0a, 0x1e, 0x43, 0x68,
+     0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74,
+     0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74,
+     0x65, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68,
+     0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74,
+     0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
+     0x6e, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63,
+     0x68, 0x69, 0x6e, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x6f, 0x62, 0x73, 0x65,
+     0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x6f, 0x62, 0x73, 0x65,
+     0x72, 0x76, 0x69, 0x6e, 0x67, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x42, 0x0a,
+     0x1e, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
+     0x6e, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x1a, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
+     0x65, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x37, 0x0a, 0x18, 0x70, 0x65, 0x6e,
+     0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+     0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x61,
+     0x73, 0x6b, 0x12, 0x5b, 0x0a, 0x2b, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65,
+     0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x63, 0x65,
+     0x65, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
+     0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x26, 0x73, 0x6b, 0x69,
+     0x70, 0x70, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x4d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x65,
+     0x64, 0x65, 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12,
+     0x4d, 0x0a, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c,
+     0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f,
+     0x5f, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65,
+     0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x73,
+     0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x4c,
+     0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x55, 0x0a, 0x0d, 0x69, 0x6e,
+     0x73, 0x69, 0x64, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+     0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
+     0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
+     0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x69, 0x6e, 0x73,
+     0x69, 0x64, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6f, 0x0a,
+     0x0d, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6d, 0x6f,
+     0x64, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4a, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
+     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64,
+     0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42, 0x65,
+     0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65,
+     0x52, 0x0c, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f,
+     0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
+     0x6e, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x0a, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x73, 0x12,
+     0x37, 0x0a, 0x18, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f,
+     0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74,
+     0x5f, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x64,
+     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x63, 0x68, 0x65, 0x64,
+     0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x15, 0x0a, 0x06,
+     0x6e, 0x6f, 0x77, 0x5f, 0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x05, 0x6e, 0x6f, 0x77, 0x55, 0x73, 0x12, 0x36, 0x0a, 0x18, 0x6e,
+     0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
+     0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18,
+     0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6e, 0x6f, 0x77, 0x54, 0x6f,
+     0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x74,
+     0x61, 0x55, 0x73, 0x12, 0x4e, 0x0a, 0x25, 0x6e, 0x6f, 0x77, 0x5f, 0x74,
+     0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73,
+     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f,
+     0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x0d, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x1f, 0x6e, 0x6f, 0x77, 0x54, 0x6f, 0x44, 0x65, 0x61,
+     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
+     0x65, 0x64, 0x41, 0x74, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
+     0x56, 0x0a, 0x15, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70,
+     0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73,
+     0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x12, 0x62, 0x65, 0x67,
+     0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41,
+     0x72, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x1a, 0x62, 0x65, 0x67, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6f, 0x62, 0x73, 0x65, 0x72,
+     0x76, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0f, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65,
+     0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65,
+     0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x17, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73,
+     0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x5f,
+     0x0a, 0x18, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x52, 0x15, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
+     0x64, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+     0x72, 0x5f, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x69, 0x73,
+     0x74, 0x6f, 0x72, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
+     0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73,
+     0x74, 0x6f, 0x72, 0x79, 0x52, 0x17, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
+     0x69, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69,
+     0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x42, 0x65,
+     0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65,
+     0x12, 0x1d, 0x0a, 0x19, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45,
+     0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
+     0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x44,
+     0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
+     0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44,
+     0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
+     0x5f, 0x49, 0x4d, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x54, 0x45, 0x10, 0x02,
+     0x12, 0x19, 0x0a, 0x15, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45,
+     0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41,
+     0x52, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x41, 0x44, 0x4c,
+     0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4c, 0x41, 0x54,
+     0x45, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x45, 0x41, 0x44, 0x4c,
+     0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x42, 0x4c, 0x4f,
+     0x43, 0x4b, 0x45, 0x44, 0x10, 0x05, 0x22, 0x86, 0x28, 0x0a, 0x1c, 0x43,
+     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
+     0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
+     0x69, 0x6e, 0x65, 0x12, 0x59, 0x0a, 0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72,
+     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x38, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
+     0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e,
+     0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a,
+     0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x59,
+     0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
+     0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x65,
      0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
      0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70,
      0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d,
-     0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74,
-     0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x3f, 0x0a, 0x1c,
-     0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x65,
-     0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19,
-     0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x42, 0x65, 0x67,
-     0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x12, 0x42, 0x0a, 0x1e, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69,
-     0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x64, 0x65,
-     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61,
-     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x37, 0x0a,
-     0x18, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x65, 0x67,
-     0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x61, 0x73,
-     0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x70, 0x65, 0x6e,
-     0x64, 0x69, 0x6e, 0x67, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x5b, 0x0a, 0x2b, 0x73, 0x6b,
-     0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x5f,
-     0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x61,
-     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x26, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x4d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x45,
-     0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c,
-     0x69, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x70,
-     0x65, 0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x5f,
-     0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x4c, 0x61,
-     0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x64,
-     0x75, 0x63, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x55,
-     0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x61, 0x63, 0x74,
-     0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f,
-     0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65,
-     0x64, 0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
-     0x0c, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f,
-     0x6e, 0x12, 0x6f, 0x0a, 0x0d, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
-     0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e,
-     0x32, 0x4a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
-     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
-     0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0c, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
-     0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65,
-     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x09, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
-     0x65, 0x55, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x65, 0x61, 0x64, 0x6c,
-     0x69, 0x6e, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
-     0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x15, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53,
-     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73,
-     0x12, 0x15, 0x0a, 0x06, 0x6e, 0x6f, 0x77, 0x5f, 0x75, 0x73, 0x18, 0x0b,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6e, 0x6f, 0x77, 0x55, 0x73, 0x12,
-     0x36, 0x0a, 0x18, 0x6e, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65,
-     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
-     0x5f, 0x75, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6e,
-     0x6f, 0x77, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
-     0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x4e, 0x0a, 0x25, 0x6e,
-     0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
-     0x6e, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64,
-     0x5f, 0x61, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73,
-     0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f, 0x6e, 0x6f, 0x77, 0x54,
-     0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x63, 0x68,
-     0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x44, 0x65, 0x6c, 0x74,
-     0x61, 0x55, 0x73, 0x12, 0x56, 0x0a, 0x15, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
-     0x61, 0x72, 0x67, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d,
-     0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52,
-     0x12, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x1a, 0x62,
-     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6f,
-     0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x52, 0x17, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x12, 0x5f, 0x0a, 0x18, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b,
-     0x32, 0x26, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x52, 0x15, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x12, 0x64, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x70, 0x6f,
-     0x73, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67,
-     0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x11, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x6f, 0x6d,
-     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e,
-     0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x17, 0x63, 0x6f,
-     0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69,
-     0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0xbe, 0x01,
-     0x0a, 0x1a, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
-     0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x44, 0x45, 0x41, 0x44,
-     0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e,
-     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
-     0x16, 0x0a, 0x12, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f,
-     0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12,
-     0x1b, 0x0a, 0x17, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f,
-     0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4d, 0x4d, 0x45, 0x44, 0x49, 0x41,
-     0x54, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x45, 0x41, 0x44,
-     0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45,
-     0x47, 0x55, 0x4c, 0x41, 0x52, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x44,
-     0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
-     0x5f, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x44,
-     0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
-     0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x05, 0x22, 0x86,
-     0x28, 0x0a, 0x1c, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
-     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x59, 0x0a, 0x0b, 0x6d,
-     0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x6f, 0x72,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0xf9, 0x0a, 0x0a, 0x0a, 0x4d, 0x61,
+     0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x51, 0x0a, 0x0b,
+     0x6e, 0x65, 0x78, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
+     0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
+     0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6e, 0x65, 0x78,
+     0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x16,
+     0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x0e, 0x32, 0x4c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
      0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43,
      0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
      0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
      0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x52, 0x0a, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x12, 0x59, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f,
-     0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x38, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x13,
+     0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x16,
+     0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x0e, 0x32, 0x4c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43,
+     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
+     0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
+     0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x13,
+     0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x8e, 0x01, 0x0a, 0x1b,
+     0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x74,
+     0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x50, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f,
+     0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74,
+     0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a,
+     0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x61, 0x79, 0x65,
+     0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69,
+     0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x17, 0x6c, 0x61, 0x79,
+     0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53,
+     0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x83, 0x01, 0x0a,
+     0x13, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x72,
+     0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01,
+     0x28, 0x0e, 0x32, 0x53, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72,
+     0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+     0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+     0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61,
+     0x77, 0x4f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52,
+     0x65, 0x64, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xa1,
+     0x01, 0x0a, 0x13, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20,
+     0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
+     0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15,
+     0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46,
+     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12,
+     0x27, 0x0a, 0x23, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50,
+     0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49,
+     0x44, 0x45, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
+     0x4d, 0x45, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x42, 0x45, 0x47, 0x49,
+     0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x41, 0x44,
+     0x4c, 0x49, 0x4e, 0x45, 0x10, 0x03, 0x22, 0x93, 0x01, 0x0a, 0x13, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45,
+     0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
+     0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+     0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49,
+     0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x42,
+     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52,
+     0x41, 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x24,
+     0x0a, 0x20, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59,
+     0x5f, 0x54, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03,
+     0x22, 0xf4, 0x01, 0x0a, 0x17, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72,
+     0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x4c, 0x41, 0x59, 0x45,
+     0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
+     0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f,
+     0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e,
+     0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x41, 0x59,
+     0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d,
+     0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x1d,
+     0x0a, 0x19, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54,
+     0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x4c, 0x41, 0x59,
+     0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d,
+     0x45, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f,
+     0x52, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x4d,
+     0x49, 0x54, 0x10, 0x04, 0x12, 0x31, 0x0a, 0x2d, 0x4c, 0x41, 0x59, 0x45,
+     0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52,
+     0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
+     0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x22, 0xc7, 0x01, 0x0a, 0x1a,
+     0x46, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77,
+     0x4f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44,
+     0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x55, 0x4e, 0x53, 0x50,
+     0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a,
+     0x12, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52,
+     0x41, 0x57, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a,
+     0x20, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52,
+     0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46,
+     0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x02, 0x12,
+     0x28, 0x0a, 0x24, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45,
+     0x44, 0x52, 0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47,
+     0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54,
+     0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e, 0x46, 0x4f, 0x52,
+     0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x57,
+     0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x44,
+     0x52, 0x41, 0x57, 0x10, 0x04, 0x1a, 0xb3, 0x1b, 0x0a, 0x0a, 0x4d, 0x69,
+     0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c,
+     0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d,
+     0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63,
+     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x4a, 0x0a,
+     0x22, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
+     0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69,
+     0x74, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x18,
+     0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x6c, 0x61, 0x73, 0x74, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75,
+     0x62, 0x6d, 0x69, 0x74, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
+     0x64, 0x12, 0x46, 0x0a, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x64,
+     0x72, 0x61, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
+     0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1c, 0x6c, 0x61, 0x73,
+     0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+     0x44, 0x72, 0x61, 0x77, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
+     0x64, 0x12, 0x52, 0x0a, 0x27, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01,
+     0x28, 0x05, 0x52, 0x21, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x65, 0x67, 0x69, 0x6e,
+     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x6e,
+     0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x69, 0x64, 0x5f, 0x64, 0x72, 0x61,
+     0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x64,
+     0x44, 0x72, 0x61, 0x77, 0x12, 0x59, 0x0a, 0x2b, 0x64, 0x69, 0x64, 0x5f,
+     0x73, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d,
+     0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f,
+     0x72, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x64,
+     0x69, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
+     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x46, 0x6f, 0x72, 0x43,
+     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12,
+     0x5f, 0x0a, 0x2e, 0x64, 0x69, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66,
+     0x79, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65,
+     0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69,
+     0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, 0x64, 0x69, 0x64,
+     0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
+     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45,
+     0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c,
+     0x12, 0x5d, 0x0a, 0x2d, 0x64, 0x69, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x69,
+     0x66, 0x79, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69,
+     0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f,
+     0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x6f,
+     0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x26, 0x64, 0x69, 0x64,
+     0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
+     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45,
+     0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x53, 0x6f, 0x6f, 0x6e, 0x12,
+     0x4b, 0x0a, 0x23, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x5f, 0x62, 0x65, 0x67,
+     0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
+     0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x77, 0x61,
+     0x6e, 0x74, 0x73, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65,
+     0x63, 0x74, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x64, 0x69, 0x64, 0x5f,
+     0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x69, 0x6e,
+     0x67, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x14, 0x64, 0x69, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+     0x44, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12,
+     0x4d, 0x0a, 0x24, 0x64, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
+     0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f,
+     0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73,
+     0x69, 0x6e, 0x6b, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x64,
+     0x69, 0x64, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65,
+     0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x48, 0x0a, 0x21, 0x64, 0x69,
+     0x64, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x69, 0x6d,
+     0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x61,
+     0x6c, 0x69, 0x64, 0x61, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x1d, 0x64, 0x69, 0x64, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72,
+     0x6d, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x76,
+     0x61, 0x6c, 0x69, 0x64, 0x61, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11,
+     0x64, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f,
+     0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x0f, 0x64, 0x69, 0x64, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54,
+     0x69, 0x6c, 0x65, 0x73, 0x12, 0x4e, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x73,
+     0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63,
+     0x6b, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x6e, 0x69,
+     0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x21, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69,
+     0x76, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x62, 0x6f, 0x61,
+     0x72, 0x64, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+     0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f,
+     0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x70, 0x65, 0x6e,
+     0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x73, 0x12, 0x63, 0x0a, 0x30, 0x73, 0x75, 0x62, 0x6d,
+     0x69, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x77, 0x69,
+     0x74, 0x68, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6c,
+     0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x11, 0x20, 0x01,
+     0x28, 0x05, 0x52, 0x29, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x73, 0x57, 0x69, 0x74, 0x68, 0x43, 0x75, 0x72, 0x72,
+     0x65, 0x6e, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a,
+     0x0c, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x72, 0x65, 0x64, 0x72, 0x61,
+     0x77, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x65, 0x65,
+     0x64, 0x73, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13,
+     0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72,
+     0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x11, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x50, 0x72, 0x65, 0x70,
+     0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16,
+     0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
+     0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x14,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x6f,
+     0x6e, 0x65, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70,
+     0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x16, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x4f, 0x6e, 0x65, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65,
+     0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x69, 0x73, 0x69,
+     0x62, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x62, 0x65, 0x67, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63,
+     0x65, 0x5f, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x16, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x61, 0x75, 0x73,
+     0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x5f, 0x64, 0x72,
+     0x61, 0x77, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x61,
+     0x6e, 0x44, 0x72, 0x61, 0x77, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x73,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x72,
+     0x61, 0x77, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65,
+     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x44, 0x72,
+     0x61, 0x77, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x70, 0x65,
+     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x1a,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x50, 0x65, 0x6e,
+     0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x12, 0x4d, 0x0a, 0x24,
+     0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65,
+     0x5f, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x66, 0x6f,
+     0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+     0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x70, 0x65, 0x6e, 0x64,
+     0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x52, 0x65, 0x61,
+     0x64, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76,
+     0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x65, 0x65, 0x64, 0x73,
+     0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18,
+     0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76,
+     0x65, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x46, 0x69,
+     0x72, 0x73, 0x74, 0x44, 0x72, 0x61, 0x77, 0x12, 0x3d, 0x0a, 0x1c, 0x61,
+     0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69,
+     0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x64,
+     0x72, 0x61, 0x77, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x61,
+     0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x52,
+     0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x44, 0x72, 0x61, 0x77, 0x12, 0x6c,
+     0x0a, 0x35, 0x64, 0x69, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
+     0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
+     0x69, 0x7a, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x61,
+     0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x1e, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x2d, 0x64, 0x69, 0x64, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+     0x41, 0x6e, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a,
+     0x65, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54,
+     0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b,
+     0x12, 0x6a, 0x0a, 0x0d, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x70, 0x72, 0x69,
+     0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0e, 0x32,
+     0x45, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
      0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
      0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74,
      0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d,
-     0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6d,
-     0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0xf9, 0x0a,
-     0x0a, 0x0a, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x12, 0x51, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x61, 0x63, 0x74,
-     0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f,
-     0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65,
-     0x64, 0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
-     0x0a, 0x6e, 0x65, 0x78, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
-     0x81, 0x01, 0x0a, 0x16, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d,
-     0x70, 0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61,
-     0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4c, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
-     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f,
-     0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x52, 0x13, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70,
-     0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
-     0x81, 0x01, 0x0a, 0x16, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61,
-     0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61,
-     0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4c, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
-     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f,
-     0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x52, 0x13, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
-     0x8e, 0x01, 0x0a, 0x1b, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72,
-     0x65, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e,
-     0x6b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
-     0x0e, 0x32, 0x50, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
-     0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-     0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e,
-     0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
-     0x17, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x12, 0x83, 0x01, 0x0a, 0x13, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x5f,
-     0x72, 0x65, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
-     0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x53, 0x2e, 0x70, 0x65, 0x72,
+     0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x54, 0x72,
+     0x65, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x0c,
+     0x74, 0x72, 0x65, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79,
+     0x12, 0x7d, 0x0a, 0x14, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x68,
+     0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
+     0x18, 0x20, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4b, 0x2e, 0x70, 0x65, 0x72,
      0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
      0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
      0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61,
-     0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52,
-     0x65, 0x64, 0x72, 0x61, 0x77, 0x4f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f,
-     0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x11, 0x66, 0x6f, 0x72,
-     0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x22, 0xa1, 0x01, 0x0a, 0x13, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f,
-     0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55,
-     0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
-     0x12, 0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d,
-     0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x44, 0x4c,
-     0x45, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x42, 0x45, 0x47, 0x49, 0x4e,
-     0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f,
-     0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e,
-     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20,
-     0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x5f,
-     0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x03, 0x22, 0x93,
-     0x01, 0x0a, 0x13, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20,
-     0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e,
-     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
-     0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15,
-     0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12,
-     0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49,
-     0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x54,
-     0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f,
-     0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x52,
-     0x45, 0x41, 0x44, 0x59, 0x5f, 0x54, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d,
-     0x49, 0x54, 0x10, 0x03, 0x22, 0xf4, 0x01, 0x0a, 0x17, 0x4c, 0x61, 0x79,
-     0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53,
-     0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c,
-     0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
-     0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x41,
-     0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41,
-     0x4d, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1b, 0x0a,
-     0x17, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f,
-     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
-     0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f,
-     0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x43,
-     0x52, 0x45, 0x41, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x2d, 0x0a,
-     0x29, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f,
-     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e,
-     0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f,
-     0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x12, 0x31, 0x0a, 0x2d,
-     0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47,
-     0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x22,
-     0xc7, 0x01, 0x0a, 0x1a, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52, 0x65,
-     0x64, 0x72, 0x61, 0x77, 0x4f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75,
-     0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x4f,
-     0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f,
-     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
-     0x00, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f,
-     0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10,
-     0x01, 0x12, 0x24, 0x0a, 0x20, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f,
-     0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49,
-     0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49,
-     0x54, 0x10, 0x02, 0x12, 0x28, 0x0a, 0x24, 0x46, 0x4f, 0x52, 0x43, 0x45,
-     0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x57, 0x41, 0x49,
-     0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x41, 0x43, 0x54,
-     0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x22, 0x0a,
-     0x1e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52,
-     0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46,
-     0x4f, 0x52, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x10, 0x04, 0x1a, 0xb3, 0x1b,
-     0x0a, 0x0a, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x63,
-     0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b,
-     0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
-     0x30, 0x0a, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65,
-     0x6e, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65,
-     0x72, 0x12, 0x4a, 0x0a, 0x22, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73,
-     0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72,
-     0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x6c,
-     0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62,
-     0x65, 0x72, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x65, 0x72, 0x66,
-     0x6f, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x6c, 0x61, 0x73,
-     0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
-     0x65, 0x72, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x66,
-     0x6f, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52,
-     0x1c, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75,
-     0x6d, 0x62, 0x65, 0x72, 0x44, 0x72, 0x61, 0x77, 0x50, 0x65, 0x72, 0x66,
-     0x6f, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x52, 0x0a, 0x27, 0x6c, 0x61, 0x73,
-     0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
-     0x65, 0x72, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69,
-     0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x74,
-     0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x21, 0x6c, 0x61, 0x73, 0x74,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42,
-     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x69, 0x64,
-     0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x07, 0x64, 0x69, 0x64, 0x44, 0x72, 0x61, 0x77, 0x12, 0x59, 0x0a, 0x2b,
-     0x64, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x65, 0x67,
-     0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
-     0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x24, 0x64, 0x69, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x42, 0x65,
+     0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48,
+     0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
+     0x12, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c,
+     0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x2d, 0x63,
+     0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
+     0x5f, 0x69, 0x73, 0x5f, 0x66, 0x61, 0x73, 0x74, 0x18, 0x21, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x26, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
+     0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x54, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
+     0x49, 0x73, 0x46, 0x61, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x20, 0x6d, 0x61,
+     0x69, 0x6e, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x69,
+     0x73, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65,
+     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x1c, 0x6d, 0x61, 0x69, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64,
+     0x4d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x44, 0x65,
+     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x5b, 0x0a, 0x2c, 0x73, 0x6b,
+     0x69, 0x70, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x5f, 0x6c,
+     0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x25, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65, 0x78, 0x74, 0x42, 0x65,
      0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x12, 0x5f, 0x0a, 0x2e, 0x64, 0x69, 0x64, 0x5f, 0x6e,
-     0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
-     0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e,
-     0x6f, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f,
-     0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x27, 0x64, 0x69, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x55,
-     0x6e, 0x74, 0x69, 0x6c, 0x12, 0x5d, 0x0a, 0x2d, 0x64, 0x69, 0x64, 0x5f,
-     0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
-     0x6e, 0x6f, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64,
-     0x5f, 0x73, 0x6f, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x26, 0x64, 0x69, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x53,
-     0x6f, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x23, 0x77, 0x61, 0x6e, 0x74, 0x73,
-     0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65, 0x78,
-     0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x1e, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74,
-     0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x17,
-     0x64, 0x69, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x64,
-     0x75, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18,
-     0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x64, 0x43, 0x6f,
-     0x6d, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x64, 0x69, 0x64, 0x5f, 0x69,
-     0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x61,
-     0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x0c, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x1f, 0x64, 0x69, 0x64, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69,
-     0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65,
-     0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x48,
-     0x0a, 0x21, 0x64, 0x69, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72,
-     0x6d, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x5f,
-     0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x69, 0x6f, 0x6e, 0x18,
-     0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x64, 0x69, 0x64, 0x50, 0x65,
-     0x72, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64,
-     0x65, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x69, 0x6f, 0x6e,
-     0x12, 0x2a, 0x0a, 0x11, 0x64, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x70,
-     0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0e, 0x20,
-     0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x64, 0x50, 0x72, 0x65, 0x70,
-     0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x4e, 0x0a, 0x23,
-     0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f,
-     0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64,
-     0x5f, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
-     0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x21, 0x63, 0x6f, 0x6e, 0x73, 0x65,
-     0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65,
-     0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x6e, 0x64,
-     0x69, 0x6e, 0x67, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52,
-     0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6d,
-     0x69, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x63, 0x0a, 0x30,
-     0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65,
-     0x6e, 0x74, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65,
-     0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b,
-     0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x29, 0x73, 0x75, 0x62, 0x6d,
-     0x69, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x57, 0x69, 0x74, 0x68,
-     0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72,
-     0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e,
-     0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x72,
-     0x65, 0x64, 0x72, 0x61, 0x77, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x0b, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77,
-     0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x70, 0x72,
-     0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18,
-     0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6e, 0x65, 0x65, 0x64, 0x73,
-     0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x73,
-     0x12, 0x33, 0x0a, 0x16, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62, 0x65,
+     0x54, 0x6f, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x4c, 0x61, 0x74, 0x65,
+     0x6e, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x18, 0x76, 0x69, 0x64, 0x65, 0x6f,
+     0x5f, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x24, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x15, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x4e, 0x65, 0x65, 0x64,
+     0x73, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73,
+     0x12, 0x33, 0x0a, 0x16, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x62, 0x65,
      0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x6e, 0x65,
-     0x65, 0x64, 0x73, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x6e, 0x65, 0x65,
-     0x64, 0x73, 0x5f, 0x6f, 0x6e, 0x65, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18,
-     0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6e, 0x65, 0x65, 0x64, 0x73,
-     0x4f, 0x6e, 0x65, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x69, 0x73,
-     0x69, 0x62, 0x6c, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
-     0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x62,
-     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73,
-     0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64,
-     0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x62, 0x65, 0x67, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
-     0x50, 0x61, 0x75, 0x73, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x61,
-     0x6e, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x07, 0x63, 0x61, 0x6e, 0x44, 0x72, 0x61, 0x77, 0x12, 0x2b, 0x0a,
-     0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x6c, 0x65, 0x73,
-     0x73, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x6c, 0x65,
-     0x73, 0x73, 0x44, 0x72, 0x61, 0x77, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x61,
-     0x73, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72,
-     0x65, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61,
-     0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65,
-     0x12, 0x4d, 0x0a, 0x24, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f,
-     0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64,
-     0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61,
-     0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f,
-     0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x49,
-     0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74,
-     0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x61,
-     0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e,
-     0x65, 0x65, 0x64, 0x73, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x64,
-     0x72, 0x61, 0x77, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61,
-     0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x65, 0x65,
-     0x64, 0x73, 0x46, 0x69, 0x72, 0x73, 0x74, 0x44, 0x72, 0x61, 0x77, 0x12,
-     0x3d, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72,
-     0x65, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f,
-     0x74, 0x6f, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x1d, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65,
-     0x65, 0x49, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x44, 0x72,
-     0x61, 0x77, 0x12, 0x6c, 0x0a, 0x35, 0x64, 0x69, 0x64, 0x5f, 0x63, 0x72,
-     0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69,
-     0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73,
-     0x74, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18,
-     0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2d, 0x64, 0x69, 0x64, 0x43, 0x72,
-     0x65, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x69,
-     0x61, 0x6c, 0x69, 0x7a, 0x65, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x61,
-     0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x53, 0x69, 0x6e, 0x6b, 0x12, 0x6a, 0x0a, 0x0d, 0x74, 0x72, 0x65, 0x65,
-     0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x1f, 0x20,
-     0x01, 0x28, 0x0e, 0x32, 0x45, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68,
-     0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74,
-     0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
-     0x6e, 0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69,
-     0x74, 0x79, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x65, 0x50, 0x72, 0x69, 0x6f,
-     0x72, 0x69, 0x74, 0x79, 0x12, 0x7d, 0x0a, 0x14, 0x73, 0x63, 0x72, 0x6f,
-     0x6c, 0x6c, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x73,
-     0x74, 0x61, 0x74, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4b,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43,
-     0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x69,
-     0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x63, 0x72,
-     0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x52, 0x12, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48,
-     0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
-     0x5d, 0x0a, 0x2d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f,
-     0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x63, 0x74, 0x69,
-     0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x61, 0x73, 0x74,
-     0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x52, 0x26, 0x63, 0x72, 0x69, 0x74,
-     0x69, 0x63, 0x61, 0x6c, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x41, 0x63, 0x74, 0x69,
-     0x76, 0x61, 0x74, 0x65, 0x49, 0x73, 0x46, 0x61, 0x73, 0x74, 0x12, 0x46,
-     0x0a, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61,
-     0x64, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x73,
-     0x74, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x22,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x6d, 0x61, 0x69, 0x6e, 0x54, 0x68,
-     0x72, 0x65, 0x61, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x4c, 0x61,
-     0x73, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x5b,
-     0x0a, 0x2c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f,
-     0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x64, 0x75,
-     0x63, 0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x23,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65,
-     0x78, 0x74, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65,
-     0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x18, 0x76,
-     0x69, 0x64, 0x65, 0x6f, 0x5f, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62,
-     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18,
-     0x24, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x76, 0x69, 0x64, 0x65, 0x6f,
-     0x4e, 0x65, 0x65, 0x64, 0x73, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x64, 0x65, 0x66, 0x65,
-     0x72, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x13, 0x64, 0x65, 0x66, 0x65, 0x72, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a,
-     0x1a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
-     0x5f, 0x68, 0x61, 0x64, 0x5f, 0x6e, 0x6f, 0x5f, 0x75, 0x70, 0x64, 0x61,
-     0x74, 0x65, 0x73, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6c,
-     0x61, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x48, 0x61, 0x64,
-     0x4e, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a,
-     0x16, 0x64, 0x69, 0x64, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e,
-     0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18,
-     0x27, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x64, 0x44, 0x72,
-     0x61, 0x77, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x12, 0x36, 0x0a, 0x18, 0x64, 0x69, 0x64, 0x5f, 0x73, 0x75, 0x62,
-     0x6d, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x14, 0x64, 0x69, 0x64, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x49, 0x6e,
-     0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a,
-     0x1c, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f,
-     0x73, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x19, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69,
-     0x64, 0x65, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69,
-     0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x21, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
-     0x74, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72,
-     0x65, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73,
-     0x69, 0x64, 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x63,
-     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e,
-     0x67, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53,
-     0x69, 0x64, 0x65, 0x12, 0x4b, 0x0a, 0x23, 0x70, 0x72, 0x65, 0x76, 0x69,
-     0x6f, 0x75, 0x73, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f,
-     0x74, 0x72, 0x65, 0x65, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x69, 0x6d, 0x70,
-     0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x1e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x65,
-     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x57, 0x61, 0x73,
-     0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x12, 0x5f, 0x0a, 0x2d,
-     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61,
-     0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x6f, 0x72,
-     0x6b, 0x6c, 0x65, 0x74, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x63,
-     0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2c, 0x20,
-     0x01, 0x28, 0x08, 0x52, 0x28, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-     0x69, 0x6e, 0x67, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x41,
-     0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x61, 0x0a,
-     0x2e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f,
-     0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x6f,
-     0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70,
-     0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18,
-     0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x70, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69,
-     0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f,
-     0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65,
-     0x12, 0x59, 0x0a, 0x2a, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69,
-     0x6e, 0x67, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x5f, 0x77, 0x6f, 0x72,
-     0x6b, 0x6c, 0x65, 0x74, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x65,
-     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2e,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
-     0x73, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x6e, 0x74, 0x57, 0x6f, 0x72,
-     0x6b, 0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x50, 0x65, 0x6e, 0x64,
-     0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x0c,
-     0x54, 0x72, 0x65, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79,
-     0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49,
-     0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
-     0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2e, 0x0a, 0x2a, 0x54,
-     0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59,
-     0x5f, 0x53, 0x41, 0x4d, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49,
-     0x54, 0x59, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x5f,
-     0x54, 0x52, 0x45, 0x45, 0x53, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x54,
-     0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59,
-     0x5f, 0x53, 0x4d, 0x4f, 0x4f, 0x54, 0x48, 0x4e, 0x45, 0x53, 0x53, 0x5f,
-     0x54, 0x41, 0x4b, 0x45, 0x53, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49,
-     0x54, 0x59, 0x10, 0x02, 0x12, 0x2c, 0x0a, 0x28, 0x54, 0x52, 0x45, 0x45,
-     0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x45,
-     0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x41,
-     0x4b, 0x45, 0x53, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59,
-     0x10, 0x03, 0x22, 0x82, 0x01, 0x0a, 0x12, 0x53, 0x63, 0x72, 0x6f, 0x6c,
-     0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f,
-     0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50,
-     0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a,
-     0x1d, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x41, 0x46, 0x46, 0x45,
-     0x43, 0x54, 0x53, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48,
-     0x41, 0x4e, 0x44, 0x4c, 0x45, 0x52, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25,
-     0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f,
-     0x4e, 0x4f, 0x54, 0x5f, 0x41, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x53,
-     0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45,
-     0x52, 0x10, 0x02, 0x22, 0x8f, 0x05, 0x0a, 0x0e, 0x42, 0x65, 0x67, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x46,
-     0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
-     0x32, 0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73,
-     0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b,
-     0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65,
-     0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
-     0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0d,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75,
-     0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x73, 0x12, 0x1f, 0x0a, 0x0b,
-     0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x73, 0x18,
-     0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x61, 0x64, 0x6c,
-     0x69, 0x6e, 0x65, 0x55, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x74,
-     0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f,
-     0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e,
-     0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55,
-     0x73, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x69, 0x74,
-     0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20,
-     0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x69,
-     0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61,
-     0x6e, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18,
-     0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6e, 0x69, 0x6d, 0x61,
-     0x74, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x73, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x48,
-     0x00, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x69, 0x64, 0x12, 0x4a, 0x0a, 0x0f,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f,
-     0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x22, 0xa2, 0x01, 0x0a, 0x12, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12,
-     0x25, 0x0a, 0x21, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
-     0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45,
-     0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
-     0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f,
-     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54,
-     0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10,
-     0x01, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59,
-     0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x02, 0x12,
-     0x20, 0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
-     0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45,
-     0x5f, 0x4d, 0x49, 0x53, 0x53, 0x45, 0x44, 0x10, 0x03, 0x42, 0x0e, 0x0a,
-     0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f,
-     0x6d, 0x22, 0xf5, 0x05, 0x0a, 0x12, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49,
-     0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73,
-     0x12, 0x22, 0x0a, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f,
-     0x61, 0x74, 0x5f, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73,
-     0x12, 0x24, 0x0a, 0x0e, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64,
-     0x5f, 0x61, 0x74, 0x5f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x0c, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x41, 0x74,
-     0x55, 0x73, 0x12, 0x3f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x6d, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x65,
+     0x66, 0x65, 0x72, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x6c, 0x61, 0x73,
+     0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x68, 0x61, 0x64,
+     0x5f, 0x6e, 0x6f, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18,
+     0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x43,
+     0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x48, 0x61, 0x64, 0x4e, 0x6f, 0x55, 0x70,
+     0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x16, 0x64, 0x69, 0x64,
+     0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73,
+     0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x27, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x12, 0x64, 0x69, 0x64, 0x44, 0x72, 0x61, 0x77, 0x49, 0x6e,
+     0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a,
+     0x18, 0x64, 0x69, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f,
+     0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x64,
+     0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x6e, 0x65, 0x65,
+     0x64, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65,
+     0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x6e, 0x65, 0x65,
+     0x64, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x49, 0x6e,
+     0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47,
+     0x0a, 0x21, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x65,
+     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69,
+     0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18,
+     0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65,
+     0x6e, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65,
+     0x65, 0x49, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x12,
+     0x4b, 0x0a, 0x23, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f,
+     0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65,
+     0x5f, 0x77, 0x61, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69,
+     0x64, 0x65, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x70, 0x72,
+     0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e,
+     0x67, 0x54, 0x72, 0x65, 0x65, 0x57, 0x61, 0x73, 0x49, 0x6d, 0x70, 0x6c,
+     0x53, 0x69, 0x64, 0x65, 0x12, 0x5f, 0x0a, 0x2d, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x69, 0x6d, 0x61,
+     0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74,
+     0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65,
+     0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x28, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x41,
+     0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b,
+     0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76,
+     0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x61, 0x0a, 0x2e, 0x70, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x69, 0x6d,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65,
+     0x74, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69,
+     0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x29, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e,
+     0x67, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f,
+     0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x50, 0x65, 0x6e,
+     0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x12, 0x59, 0x0a, 0x2a,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70,
+     0x61, 0x69, 0x6e, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74,
+     0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e,
+     0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x25, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67,
+     0x50, 0x61, 0x69, 0x6e, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74,
+     0x73, 0x46, 0x6f, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54,
+     0x72, 0x65, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x65, 0x65,
+     0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x19,
+     0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54,
+     0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+     0x44, 0x10, 0x00, 0x12, 0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x45, 0x45, 0x5f,
+     0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x41, 0x4d,
+     0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x46,
+     0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x5f, 0x54, 0x52, 0x45, 0x45,
+     0x53, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x54, 0x52, 0x45, 0x45, 0x5f,
+     0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x4d, 0x4f,
+     0x4f, 0x54, 0x48, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x41, 0x4b, 0x45,
+     0x53, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x10, 0x02,
+     0x12, 0x2c, 0x0a, 0x28, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49,
+     0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x45, 0x57, 0x5f, 0x43, 0x4f,
+     0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x41, 0x4b, 0x45, 0x53, 0x5f,
+     0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x10, 0x03, 0x22, 0x82,
+     0x01, 0x0a, 0x12, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e,
+     0x64, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a,
+     0x1a, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44,
+     0x4c, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
+     0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x52,
+     0x4f, 0x4c, 0x4c, 0x5f, 0x41, 0x46, 0x46, 0x45, 0x43, 0x54, 0x53, 0x5f,
+     0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c,
+     0x45, 0x52, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, 0x53, 0x43, 0x52, 0x4f,
+     0x4c, 0x4c, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f,
+     0x41, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c,
+     0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x52, 0x10, 0x02, 0x22,
+     0x8f, 0x05, 0x0a, 0x0e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x46, 0x0a, 0x04, 0x74, 0x79,
+     0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x54, 0x79, 0x70, 0x65,
+     0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f,
+     0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x04, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12,
+     0x27, 0x0a, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f,
+     0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
+     0x52, 0x0e, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75,
+     0x6d, 0x62, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69,
+     0x6d, 0x65, 0x55, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x61, 0x64,
+     0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x0a, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x55,
+     0x73, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
+     0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x06,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
+     0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x28, 0x0a,
+     0x10, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
+     0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x0e, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50,
+     0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6e, 0x69, 0x6d, 0x61,
+     0x74, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x0b, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x4f, 0x6e,
+     0x6c, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+     0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x69,
+     0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x11, 0x73,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x49, 0x69, 0x64, 0x12, 0x4a, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72,
+     0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+     0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x63,
-     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18,
-     0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
-     0x67, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
-     0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x3e, 0x0a, 0x09, 0x6c, 0x61, 0x73,
-     0x74, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
-     0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x48, 0x00, 0x52,
-     0x08, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x5c, 0x0a,
-     0x10, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x5f,
-     0x69, 0x6e, 0x5f, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49,
-     0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73,
-     0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49,
-     0x6e, 0x55, 0x73, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
-     0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x1a, 0xad, 0x02, 0x0a, 0x0e,
-     0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49, 0x6e,
-     0x55, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
-     0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
-     0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x15, 0x6e, 0x6f, 0x77,
-     0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
-     0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x12, 0x6e, 0x6f, 0x77, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c,
-     0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x17,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74,
-     0x6f, 0x5f, 0x6e, 0x6f, 0x77, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x54, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4e, 0x6f, 0x77, 0x44, 0x65, 0x6c,
-     0x74, 0x61, 0x12, 0x3e, 0x0a, 0x1c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
-     0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64,
-     0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54,
-     0x69, 0x6d, 0x65, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
-     0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f,
-     0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6e, 0x6f, 0x77,
-     0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69,
-     0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64,
-     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22,
-     0x38, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x14,
-     0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f,
-     0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15,
-     0x0a, 0x11, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d,
-     0x45, 0x5f, 0x55, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x42, 0x06, 0x0a,
-     0x04, 0x61, 0x72, 0x67, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x17, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65,
-     0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a,
-     0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x65, 0x67,
-     0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67,
-     0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x64, 0x72, 0x6f,
-     0x70, 0x70, 0x65, 0x64, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x52, 0x0a, 0x15, 0x6c, 0x61,
-     0x73, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69,
+     0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+     0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x01, 0x0a,
+     0x12, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41,
+     0x72, 0x67, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x42,
+     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41,
+     0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53,
+     0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21,
+     0x0a, 0x1d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d,
+     0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
+     0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a,
+     0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e,
+     0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x42,
+     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41,
+     0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x49, 0x53,
+     0x53, 0x45, 0x44, 0x10, 0x03, 0x42, 0x0e, 0x0a, 0x0c, 0x63, 0x72, 0x65,
+     0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0xf5, 0x05,
+     0x0a, 0x12, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d,
+     0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75,
+     0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x75, 0x70, 0x64,
+     0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x24, 0x0a, 0x0e,
+     0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f,
+     0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x66, 0x69,
+     0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x3f,
+     0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x0e, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69,
+     0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
+     0x67, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74,
+     0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65,
+     0x6e, 0x74, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
      0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
      0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x12,
-     0x6c, 0x61, 0x73, 0x74, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xc5, 0x01, 0x0a, 0x15, 0x42,
-     0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75,
-     0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
-     0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x61, 0x75, 0x73, 0x65,
-     0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x62, 0x73,
-     0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d,
-     0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65,
-     0x72, 0x73, 0x12, 0x52, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x62,
+     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x48, 0x00,
+     0x52, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x67,
+     0x73, 0x12, 0x3e, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x72,
+     0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x41, 0x72, 0x67, 0x73, 0x48, 0x00, 0x52, 0x08, 0x6c, 0x61, 0x73,
+     0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x10, 0x74, 0x69, 0x6d,
+     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75,
+     0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x54, 0x69, 0x6d,
+     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x52,
+     0x0e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49,
+     0x6e, 0x55, 0x73, 0x1a, 0xad, 0x02, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65,
+     0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x12, 0x25,
+     0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x64,
+     0x65, 0x6c, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d,
+     0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74,
+     0x61, 0x12, 0x31, 0x0a, 0x15, 0x6e, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f,
+     0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c,
+     0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6e, 0x6f,
+     0x77, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44,
+     0x65, 0x6c, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x17, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f,
+     0x77, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x13, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65,
+     0x54, 0x6f, 0x4e, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x3e,
+     0x0a, 0x1c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+     0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
+     0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x18, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x54,
+     0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c,
+     0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x03, 0x6e, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54,
+     0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c,
+     0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64,
+     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x38, 0x0a, 0x05, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x42, 0x45, 0x47, 0x49,
+     0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x49,
+     0x53, 0x48, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x45,
+     0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x53,
+     0x49, 0x4e, 0x47, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x72, 0x67,
+     0x73, 0x22, 0xa6, 0x01, 0x0a, 0x17, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x72, 0x6f,
+     0x70, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x15, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64,
+     0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
+     0x67, 0x73, 0x12, 0x52, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x62,
      0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61,
-     0x72, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
+     0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
      0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
      0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74,
      0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
-     0x67, 0x73, 0x22, 0xfd, 0x04, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
-     0x73, 0x69, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48,
-     0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x31, 0x62, 0x65,
-     0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x63, 0x72, 0x69,
-     0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61,
-     0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x2a, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x51, 0x75, 0x65,
-     0x75, 0x65, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x73,
-     0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55,
-     0x73, 0x12, 0x6c, 0x0a, 0x35, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d,
-     0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x71, 0x75,
-     0x65, 0x75, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x63, 0x72, 0x69, 0x74,
-     0x69, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74,
-     0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x2d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d,
-     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x51, 0x75, 0x65, 0x75,
-     0x65, 0x4e, 0x6f, 0x74, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-     0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74,
-     0x61, 0x55, 0x73, 0x12, 0x76, 0x0a, 0x3b, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
-     0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x61,
-     0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+     0x67, 0x73, 0x22, 0xc5, 0x01, 0x0a, 0x15, 0x42, 0x65, 0x67, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72,
+     0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a,
+     0x06, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x06, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x12, 0x23, 0x0a,
+     0x0d, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65,
+     0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75,
+     0x6d, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x52,
+     0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18,
+     0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
+     0x67, 0x73, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x42, 0x65, 0x67, 0x69,
+     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xfd,
+     0x04, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+     0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f,
+     0x72, 0x79, 0x12, 0x65, 0x0a, 0x31, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
+     0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x71,
+     0x75, 0x65, 0x75, 0x65, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
+     0x6c, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64,
+     0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x2a, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x43, 0x72,
+     0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61,
+     0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x6c, 0x0a,
+     0x35, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f,
+     0x6e, 0x6f, 0x74, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
      0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65,
-     0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x31, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x6f, 0x52,
-     0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
-     0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74,
-     0x61, 0x55, 0x73, 0x12, 0x5d, 0x0a, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
+     0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x2d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4e, 0x6f, 0x74,
+     0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x73, 0x74, 0x69,
+     0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
+     0x76, 0x0a, 0x3b, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69,
+     0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72,
      0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74,
-     0x6f, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x65,
-     0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74,
-     0x61, 0x5f, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x26,
-     0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x61, 0x64,
-     0x79, 0x54, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x45,
+     0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x73, 0x74,
+     0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f,
+     0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x31, 0x62, 0x65,
+     0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79,
+     0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x45, 0x73, 0x74, 0x69,
+     0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
+     0x5d, 0x0a, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x74, 0x6f,
+     0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x63,
+     0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d,
+     0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73,
+     0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x26, 0x63, 0x6f, 0x6d, 0x6d,
+     0x69, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x41,
+     0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x45, 0x73, 0x74, 0x69, 0x6d,
+     0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x44,
+     0x0a, 0x1f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69,
+     0x6c, 0x65, 0x73, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65,
+     0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x1b, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65,
+     0x54, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74,
+     0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x3b, 0x0a, 0x1a,
+     0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x74,
+     0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f,
+     0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x61, 0x63,
+     0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61,
+     0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x33, 0x0a,
+     0x16, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61,
+     0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18,
+     0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x64, 0x72, 0x61, 0x77, 0x45,
      0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61,
-     0x55, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72,
-     0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x65, 0x73, 0x74, 0x69,
-     0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75,
-     0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x70, 0x72, 0x65,
-     0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x73, 0x74,
-     0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73,
-     0x12, 0x3b, 0x0a, 0x1a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
-     0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65,
-     0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x45, 0x73,
-     0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55,
-     0x73, 0x12, 0x33, 0x0a, 0x16, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x73,
-     0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
-     0x5f, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x64,
-     0x72, 0x61, 0x77, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44,
-     0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x2a, 0xb0, 0x05, 0x0a, 0x1f, 0x43,
-     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
-     0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72,
-     0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x43,
-     0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
-     0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x43,
-     0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f,
-     0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10,
-     0x01, 0x12, 0x2d, 0x0a, 0x29, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45,
-     0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e,
-     0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f,
-     0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02,
-     0x12, 0x1e, 0x0a, 0x1a, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44,
-     0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
-     0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x12, 0x2a, 0x0a, 0x26,
-     0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52,
-     0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x43, 0x54, 0x49,
-     0x56, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x54, 0x52,
-     0x45, 0x45, 0x10, 0x04, 0x12, 0x28, 0x0a, 0x24, 0x43, 0x43, 0x5f, 0x53,
-     0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54,
-     0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x49, 0x46, 0x5f,
-     0x50, 0x4f, 0x53, 0x53, 0x49, 0x42, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x23,
-     0x0a, 0x1f, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
-     0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52,
-     0x41, 0x57, 0x5f, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x10, 0x06, 0x12,
-     0x22, 0x0a, 0x1e, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55,
-     0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44,
-     0x52, 0x41, 0x57, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x10, 0x07, 0x12,
-     0x3c, 0x0a, 0x38, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55,
-     0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42,
-     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54,
-     0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x53, 0x49,
-     0x4e, 0x4b, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10,
-     0x08, 0x12, 0x25, 0x0a, 0x21, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45,
-     0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e,
-     0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x45, 0x5f, 0x54, 0x49, 0x4c,
-     0x45, 0x53, 0x10, 0x09, 0x12, 0x38, 0x0a, 0x34, 0x43, 0x43, 0x5f, 0x53,
-     0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54,
-     0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41,
-     0x54, 0x45, 0x5f, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45,
-     0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x4b,
-     0x10, 0x0a, 0x12, 0x36, 0x0a, 0x32, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
+     0x55, 0x73, 0x2a, 0xb0, 0x05, 0x0a, 0x1f, 0x43, 0x68, 0x72, 0x6f, 0x6d,
+     0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
+     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69,
+     0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
      0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f,
-     0x4e, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x49, 0x4d,
-     0x50, 0x4c, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41,
-     0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x0b, 0x12, 0x42,
-     0x0a, 0x3e, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
-     0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f,
-     0x54, 0x49, 0x46, 0x59, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d,
-     0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e, 0x4f,
-     0x54, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x55,
-     0x4e, 0x54, 0x49, 0x4c, 0x10, 0x0c, 0x12, 0x41, 0x0a, 0x3d, 0x43, 0x43,
+     0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+     0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x43, 0x5f, 0x53, 0x43,
+     0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49,
+     0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x2d, 0x0a,
+     0x29, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45,
+     0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x4e,
+     0x44, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a,
+     0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52,
+     0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d,
+     0x49, 0x54, 0x10, 0x03, 0x12, 0x2a, 0x0a, 0x26, 0x43, 0x43, 0x5f, 0x53,
+     0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54,
+     0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45,
+     0x5f, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x10, 0x04,
+     0x12, 0x28, 0x0a, 0x24, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44,
+     0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
+     0x44, 0x52, 0x41, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x50, 0x4f, 0x53, 0x53,
+     0x49, 0x42, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x43,
+     0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41,
+     0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x46,
+     0x4f, 0x52, 0x43, 0x45, 0x44, 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x43,
+     0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f,
+     0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f,
+     0x41, 0x42, 0x4f, 0x52, 0x54, 0x10, 0x07, 0x12, 0x3c, 0x0a, 0x38, 0x43,
+     0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f,
+     0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e,
+     0x5f, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f,
+     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x4b, 0x5f, 0x43,
+     0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x25, 0x0a,
+     0x21, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45,
+     0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45,
+     0x50, 0x41, 0x52, 0x45, 0x5f, 0x54, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x09,
+     0x12, 0x38, 0x0a, 0x34, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44,
+     0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
+     0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4c,
+     0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52,
+     0x41, 0x4d, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x4b, 0x10, 0x0a, 0x12, 0x36,
+     0x0a, 0x32, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
+     0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45,
+     0x52, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x53,
+     0x49, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41,
+     0x54, 0x49, 0x4f, 0x4e, 0x10, 0x0b, 0x12, 0x42, 0x0a, 0x3e, 0x43, 0x43,
      0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41,
      0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59,
      0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f,
      0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x58,
-     0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10,
-     0x0d, 0x42, 0x02, 0x48, 0x03}};
+     0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x54, 0x49, 0x4c,
+     0x10, 0x0c, 0x12, 0x41, 0x0a, 0x3d, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
+     0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f,
+     0x4e, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59, 0x5f, 0x42, 0x45, 0x47,
+     0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d,
+     0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54,
+     0x45, 0x44, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0d}};
 
 }  // namespace perfetto
 
diff --git a/src/trace_processor/importers/proto/graphics_event_module.cc b/src/trace_processor/importers/proto/graphics_event_module.cc
index 38979f2..f003464 100644
--- a/src/trace_processor/importers/proto/graphics_event_module.cc
+++ b/src/trace_processor/importers/proto/graphics_event_module.cc
@@ -57,7 +57,7 @@
                                      decoder.vulkan_memory_event());
       return;
     case TracePacket::kVulkanApiEventFieldNumber:
-      parser_.ParseVulkanApiEvent(decoder.vulkan_api_event());
+      parser_.ParseVulkanApiEvent(ttp.timestamp, decoder.vulkan_api_event());
       return;
   }
 }
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.cc b/src/trace_processor/importers/proto/graphics_event_parser.cc
index 1805731..ed87c84 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.cc
+++ b/src/trace_processor/importers/proto/graphics_event_parser.cc
@@ -16,12 +16,16 @@
 
 #include "src/trace_processor/importers/proto/graphics_event_parser.h"
 
+#include <inttypes.h>
+
+#include "perfetto/ext/base/utils.h"
 #include "perfetto/protozero/field.h"
 #include "src/trace_processor/args_tracker.h"
 #include "src/trace_processor/event_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
 #include "src/trace_processor/track_tracker.h"
 
 #include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
@@ -136,7 +140,10 @@
                          context_->storage->InternString("WARNING"),
                          context_->storage->InternString("ERROR"),
                          context_->storage->InternString(
-                             "UNKNOWN_SEVERITY") /* must be last */}} {}
+                             "UNKNOWN_SEVERITY") /* must be last */}},
+      vk_event_track_id_(context->storage->InternString("Vulkan Events")),
+      vk_event_scope_id_(context->storage->InternString("vulkan_events")),
+      vk_queue_submit_id_(context->storage->InternString("vkQueueSubmit")) {}
 
 void GraphicsEventParser::ParseGpuCounterEvent(int64_t ts, ConstBytes blob) {
   protos::pbzero::GpuCounterEvent::Decoder event(blob.data, blob.size);
@@ -162,7 +169,7 @@
         gpu_counter_track_ids_.end()) {
       auto desc = spec.description();
 
-      StringId unit_id = 0;
+      StringId unit_id = kNullStringId;
       if (spec.has_numerator_units() || spec.has_denominator_units()) {
         char buffer[1024];
         base::StringWriter unit(buffer, sizeof(buffer));
@@ -226,7 +233,7 @@
 }
 
 const StringId GraphicsEventParser::GetFullStageName(
-    const protos::pbzero::GpuRenderStageEvent_Decoder& event) {
+    const protos::pbzero::GpuRenderStageEvent_Decoder& event) const {
   size_t stage_id = static_cast<size_t>(event.stage_id());
   StringId stage_name;
 
@@ -237,27 +244,6 @@
     snprintf(buffer, sizeof(buffer), "render stage(%zu)", stage_id);
     stage_name = context_->storage->InternString(buffer);
   }
-  // If the slice has a render target handle, we append the hex value of the
-  // handle to the name.  If a debug marker is available, we append the name
-  // of the render target.
-  if (event.has_render_target_handle()) {
-    char buffer[256];
-    base::StringWriter str_writer(buffer, sizeof(buffer));
-    str_writer.AppendString(context_->storage->GetString(stage_name));
-    auto framebuffer_names =
-        debug_marker_names_[static_cast<int32_t>(VK_OBJECT_TYPE_FRAMEBUFFER)];
-    auto debug_marker_name =
-        framebuffer_names.find(event.render_target_handle());
-    str_writer.AppendChar('[');
-    if (debug_marker_name == framebuffer_names.end()) {
-      str_writer.AppendLiteral("0x");
-      str_writer.AppendHexInt(event.render_target_handle());
-    } else {
-      str_writer.AppendString(base::StringView(debug_marker_name->second));
-    }
-    str_writer.AppendChar(']');
-    stage_name = context_->storage->InternString(str_writer.GetStringView());
-  }
   return stage_name;
 }
 
@@ -271,7 +257,7 @@
   StringId track_name = context_->storage->InternString(hw_queue.name());
   if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size() ||
       !gpu_hw_queue_ids_[gpu_hw_queue_counter_].has_value()) {
-    tables::GpuTrackTable::Row track(track_name.id);
+    tables::GpuTrackTable::Row track(track_name);
     track.scope = gpu_render_stage_scope_id_;
     track.description = context_->storage->InternString(hw_queue.description());
     if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size()) {
@@ -298,7 +284,7 @@
       context_->storage->mutable_gpu_track_table()->mutable_description()->Set(
           row, context_->storage->InternString(hw_queue.description()));
     } else {
-      tables::GpuTrackTable::Row track(track_name.id);
+      tables::GpuTrackTable::Row track(track_name);
       track.scope = gpu_render_stage_scope_id_;
       track.description =
           context_->storage->InternString(hw_queue.description());
@@ -306,6 +292,21 @@
   }
   ++gpu_hw_queue_counter_;
 }
+base::Optional<std::string> GraphicsEventParser::FindDebugName(
+    int32_t vk_object_type,
+    uint64_t vk_handle) const {
+  auto map = debug_marker_names_.find(vk_object_type);
+  if (map == debug_marker_names_.end()) {
+    return base::nullopt;
+  }
+
+  auto name = map->second.find(vk_handle);
+  if (name == map->second.end()) {
+    return base::nullopt;
+  } else {
+    return name->second;
+  }
+}
 
 void GraphicsEventParser::ParseGpuRenderStageEvent(int64_t ts,
                                                    ConstBytes blob) {
@@ -373,23 +374,45 @@
       }
       StringId track_name =
           context_->storage->InternString(writer.GetStringView());
-      tables::GpuTrackTable::Row track(track_name.id);
+      tables::GpuTrackTable::Row track(track_name);
       track.scope = gpu_render_stage_scope_id_;
       track_id = context_->track_tracker->InternGpuTrack(track);
       gpu_hw_queue_ids_.resize(hw_queue_id + 1);
       gpu_hw_queue_ids_[hw_queue_id] = track_id;
     }
 
-    StringId stage_name = GetFullStageName(event);
-    const auto slice_id = context_->slice_tracker->Scoped(
-        ts, track_id, 0 /* cat */, stage_name,
-        static_cast<int64_t>(event.duration()), args_callback);
+    auto render_target_name = FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
+    auto render_target_name_id = render_target_name.has_value()
+                                  ? context_->storage->InternString(
+                                      render_target_name.value().c_str())
+                                  : kNullStringId;
+    auto render_pass_name = FindDebugName(VK_OBJECT_TYPE_RENDER_PASS, event.render_pass_handle());
+    auto render_pass_name_id = render_pass_name.has_value()
+                                  ? context_->storage->InternString(
+                                      render_pass_name.value().c_str())
+                                  : kNullStringId;
+    auto command_buffer_name = FindDebugName(VK_OBJECT_TYPE_COMMAND_BUFFER, event.command_buffer_handle());
+    auto command_buffer_name_id = command_buffer_name.has_value()
+                                  ? context_->storage->InternString(
+                                      command_buffer_name.value().c_str())
+                                  : kNullStringId;
 
-    context_->storage->mutable_gpu_slice_table()->Insert(
-        tables::GpuSliceTable::Row(
-            slice_id.value(), static_cast<int64_t>(event.context()),
-            static_cast<int64_t>(event.render_target_handle()),
-            base::nullopt /*frame_id*/, event.submission_id(), hw_queue_id));
+    tables::GpuSliceTable::Row row;
+    row.ts = ts;
+    row.track_id = track_id.value;
+    row.name = GetFullStageName(event);
+    row.dur = static_cast<int64_t>(event.duration());
+    row.context_id = static_cast<int64_t>(event.context());
+    row.render_target = static_cast<int64_t>(event.render_target_handle());
+    row.render_target_name = render_target_name_id;
+    row.render_pass = static_cast<int64_t>(event.render_pass_handle());
+    row.render_pass_name = render_pass_name_id;
+    row.command_buffer = static_cast<int64_t>(event.command_buffer_handle());
+    row.command_buffer_name = command_buffer_name_id;
+    row.submission_id = event.submission_id();
+    row.hw_queue_id = hw_queue_id;
+
+    context_->slice_tracker->ScopedGpu(row, args_callback);
   }
 }
 
@@ -452,23 +475,23 @@
   const uint32_t frame_number =
       event.has_frame_number() ? event.frame_number() : 0;
 
-  tables::GpuTrackTable::Row track(track_name_id.id);
+  tables::GpuTrackTable::Row track(track_name_id);
   track.scope = graphics_event_scope_id_;
   TrackId track_id = context_->track_tracker->InternGpuTrack(track);
 
-  auto scoped_callback = [this,
-                          layer_name_id](ArgsTracker::BoundInserter* inserter) {
-    inserter->AddArg(layer_name_key_id_, Variadic::String(layer_name_id));
-  };
-  auto opt_slice_id =
-      context_->slice_tracker->Scoped(timestamp, track_id, 0 /* cat */,
-                                      event_name_id, duration, scoped_callback);
+  {
+    auto scoped_callback =
+        [this, layer_name_id](ArgsTracker::BoundInserter* inserter) {
+          inserter->AddArg(layer_name_key_id_, Variadic::String(layer_name_id));
+        };
 
-  if (opt_slice_id) {
     tables::GpuSliceTable::Row row;
-    row.slice_id = opt_slice_id.value();
+    row.ts = timestamp;
+    row.track_id = track_id.value;
+    row.name = event_name_id;
+    row.dur = duration;
     row.frame_id = frame_number;
-    context_->storage->mutable_gpu_slice_table()->Insert(row);
+    context_->slice_tracker->ScopedGpu(row, scoped_callback);
   }
 
   /* Displayed Frame track */
@@ -476,7 +499,7 @@
     if (previous_timestamp_ == 0) {
       const StringId present_track_name_id =
           context_->storage->InternString("Displayed Frame");
-      tables::GpuTrackTable::Row present_track(present_track_name_id.id);
+      tables::GpuTrackTable::Row present_track(present_track_name_id);
       present_track.scope = graphics_event_scope_id_;
       present_track_id_ =
           context_->track_tracker->InternGpuTrack(present_track);
@@ -508,17 +531,18 @@
                            Variadic::String(present_frame_layer_name_id));
         };
         // End the current slice that's being tracked.
-        const auto present_slice_id_end =
-            context_->slice_tracker->End(timestamp, present_track_id_, 0,
-                                         present_event_name_id_, args_callback);
+        const auto opt_slice_id = context_->slice_tracker->EndGpu(
+            timestamp, present_track_id_, args_callback);
 
-        if (present_slice_id_end) {
+        if (opt_slice_id) {
           // The slice could have had additional buffers in it, so we need to
           // update the name.
-          context_->storage->mutable_slice_table()->mutable_name()->Set(
-              present_slice_id_end.value(),
-              context_->storage->InternString(
-                  present_frame_name_.GetStringView()));
+          auto* slice_table = context_->storage->mutable_slice_table();
+
+          uint32_t row_idx = *slice_table->id().IndexOf(*opt_slice_id);
+          StringId frame_name_id = context_->storage->InternString(
+              present_frame_name_.GetStringView());
+          slice_table->mutable_name()->Set(row_idx, frame_name_id);
         }
 
         present_frame_layer_name_.reset();
@@ -531,15 +555,13 @@
       present_frame_layer_name_.AppendString(event.layer_name());
       present_event_name_id_ =
           context_->storage->InternString(present_frame_name_.GetStringView());
-      const auto present_slice_id = context_->slice_tracker->Begin(
-          timestamp, present_track_id_, 0 /* cat */, present_event_name_id_);
 
-      if (present_slice_id) {
-        tables::GpuSliceTable::Row row;
-        row.slice_id = present_slice_id.value();
-        row.frame_id = frame_number;
-        context_->storage->mutable_gpu_slice_table()->Insert(row);
-      }
+      tables::GpuSliceTable::Row row;
+      row.ts = timestamp;
+      row.track_id = present_track_id_.value;
+      row.name = present_event_name_id_;
+      row.frame_id = frame_number;
+      context_->slice_tracker->BeginGpu(row);
     }
   }
 }
@@ -555,6 +577,8 @@
     case VulkanMemoryEvent::SOURCE_DRIVER:
       allocation_scope = static_cast<VulkanMemoryEvent::AllocationScope>(
           event.allocation_scope());
+      if (allocation_scope == VulkanMemoryEvent::SCOPE_UNSPECIFIED)
+        return;
       switch (event.operation()) {
         case VulkanMemoryEvent::OP_CREATE:
           vulkan_driver_memory_counters_[allocation_scope] +=
@@ -689,12 +713,10 @@
                                        vulkan_memory_event);
 
   auto* allocs = context_->storage->mutable_vulkan_memory_allocations_table();
-  auto id = allocs->Insert(vulkan_memory_event_row);
-  uint32_t row = *allocs->id().IndexOf(id);
+  VulkanAllocId id = allocs->Insert(vulkan_memory_event_row);
 
   if (vulkan_memory_event.has_annotations()) {
-    ArgsTracker::BoundInserter inserter(context_->args_tracker.get(),
-                                        TableId::kVulkanMemoryAllocation, row);
+    auto inserter = context_->args_tracker->AddArgsTo(id);
 
     for (auto it = vulkan_memory_event.annotations(); it; ++it) {
       protos::pbzero::VulkanMemoryEventAnnotation::Decoder annotation(*it);
@@ -715,7 +737,7 @@
                     sequence_state,
                     static_cast<uint64_t>(annotation.string_iid()));
 
-        inserter.AddArg(key_id, Variadic::String(string_id.id));
+        inserter.AddArg(key_id, Variadic::String(string_id));
       }
     }
   }
@@ -724,7 +746,7 @@
 void GraphicsEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) {
   protos::pbzero::GpuLog::Decoder event(blob.data, blob.size);
 
-  tables::GpuTrackTable::Row track(gpu_log_track_name_id_.id);
+  tables::GpuTrackTable::Row track(gpu_log_track_name_id_);
   track.scope = gpu_log_scope_id_;
   TrackId track_id = context_->track_tracker->InternGpuTrack(track);
 
@@ -746,15 +768,16 @@
       severity < log_severity_ids_.size()
           ? log_severity_ids_[static_cast<size_t>(event.severity())]
           : log_severity_ids_[log_severity_ids_.size() - 1];
-  const auto slice_id = context_->slice_tracker->Scoped(
-      ts, track_id, 0 /* cat */, severity_id, 0 /* duration */, args_callback);
 
   tables::GpuSliceTable::Row row;
-  row.slice_id = slice_id.value();
-  context_->storage->mutable_gpu_slice_table()->Insert(row);
+  row.ts = ts;
+  row.track_id = track_id.value;
+  row.name = severity_id;
+  row.dur = 0;
+  context_->slice_tracker->ScopedGpu(row, args_callback);
 }
 
-void GraphicsEventParser::ParseVulkanApiEvent(ConstBytes blob) {
+void GraphicsEventParser::ParseVulkanApiEvent(int64_t ts, ConstBytes blob) {
   protos::pbzero::VulkanApiEvent::Decoder vk_event(blob.data, blob.size);
   if (vk_event.has_vk_debug_utils_object_name()) {
     protos::pbzero::VulkanApiEvent_VkDebugUtilsObjectName::Decoder event(
@@ -762,6 +785,32 @@
     debug_marker_names_[event.object_type()][event.object()] =
         event.object_name().ToStdString();
   }
+  if (vk_event.has_vk_queue_submit()) {
+    protos::pbzero::VulkanApiEvent_VkQueueSubmit::Decoder event(
+        vk_event.vk_queue_submit());
+    // Once flow table is implemented, we can create a nice UI that link the
+    // vkQueueSubmit to GpuRenderStageEvent.  For now, just add it as in a GPU
+    // track so that they can appear close to the render stage slices.
+    tables::GpuTrackTable::Row track(vk_event_track_id_);
+    track.scope = vk_event_scope_id_;
+    TrackId track_id = context_->track_tracker->InternGpuTrack(track);
+    tables::GpuSliceTable::Row row;
+    row.ts = ts;
+    row.dur = static_cast<int64_t>(event.duration_ns());
+    row.track_id = track_id.value;
+    row.name = vk_queue_submit_id_;
+    if (event.has_vk_command_buffers()) {
+      row.command_buffer = static_cast<int64_t>(*event.vk_command_buffers());
+    }
+    row.submission_id = event.submission_id();
+    auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) {
+      inserter->AddArg(context_->storage->InternString("pid"),
+                       Variadic::Integer(event.pid()));
+      inserter->AddArg(context_->storage->InternString("tid"),
+                       Variadic::Integer(event.tid()));
+    };
+    context_->slice_tracker->ScopedGpu(row, args_callback);
+  }
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.h b/src/trace_processor/importers/proto/graphics_event_parser.h
index 73b8991..10bf5e5 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.h
+++ b/src/trace_processor/importers/proto/graphics_event_parser.h
@@ -22,6 +22,7 @@
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/protozero/field.h"
 #include "protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.h"
+#include "src/trace_processor/args_tracker.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
 #include "src/trace_processor/importers/proto/vulkan_memory_tracker.h"
 #include "src/trace_processor/trace_storage.h"
@@ -66,14 +67,16 @@
   void UpdateVulkanMemoryAllocationCounters(UniquePid,
                                             const VulkanMemoryEvent::Decoder&);
 
-  void ParseVulkanApiEvent(ConstBytes);
+  void ParseVulkanApiEvent(int64_t, ConstBytes);
 
  private:
   const StringId GetFullStageName(
-      const protos::pbzero::GpuRenderStageEvent_Decoder& event);
+      const protos::pbzero::GpuRenderStageEvent_Decoder& event) const;
   void InsertGpuTrack(
       const protos::pbzero::
           GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue);
+  base::Optional<std::string> FindDebugName(int32_t vk_object_type,
+                                            uint64_t vk_handle) const;
 
   TraceProcessorContext* const context_;
   VulkanMemoryTracker vulkan_memory_tracker_;
@@ -115,10 +118,15 @@
   const StringId log_message_id_;
   std::array<StringId, 7> log_severity_ids_;
   // For Vulkan events.
+  // For VulkanApiEvent.VkDebugUtilsObjectName.
   // Map of vk handle -> vk object name.
   using DebugMarkerMap = std::unordered_map<uint64_t, std::string>;
   // Map of VkObjectType -> DebugMarkerMap.
   std::unordered_map<int32_t, DebugMarkerMap> debug_marker_names_;
+  // For VulkanApiEvent.VkQueueSubmit.
+  StringId vk_event_track_id_;
+  StringId vk_event_scope_id_;
+  StringId vk_queue_submit_id_;
 };
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/heap_graph_module.cc b/src/trace_processor/importers/proto/heap_graph_module.cc
index 49d2f02..8da6932 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -92,7 +92,7 @@
 using perfetto::protos::pbzero::TracePacket;
 
 HeapGraphModule::HeapGraphModule(TraceProcessorContext* context)
-    : context_(context), heap_graph_tracker_(context) {
+    : context_(context) {
   RegisterForField(TracePacket::kHeapGraphFieldNumber, context);
   RegisterForField(TracePacket::kDeobfuscationMappingFieldNumber, context);
 }
@@ -115,10 +115,11 @@
 void HeapGraphModule::ParseHeapGraph(uint32_t seq_id,
                                      int64_t ts,
                                      protozero::ConstBytes blob) {
+  auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
   protos::pbzero::HeapGraph::Decoder heap_graph(blob.data, blob.size);
   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
       static_cast<uint32_t>(heap_graph.pid()));
-  heap_graph_tracker_.SetPacketIndex(seq_id, heap_graph.index());
+  heap_graph_tracker->SetPacketIndex(seq_id, heap_graph.index());
   for (auto it = heap_graph.objects(); it; ++it) {
     protos::pbzero::HeapGraphObject::Decoder object(*it);
     HeapGraphTracker::SourceObject obj;
@@ -156,14 +157,14 @@
       ref.owned_object_id = object_ids[i];
       obj.references.emplace_back(std::move(ref));
     }
-    heap_graph_tracker_.AddObject(seq_id, upid, ts, std::move(obj));
+    heap_graph_tracker->AddObject(seq_id, upid, ts, std::move(obj));
   }
   for (auto it = heap_graph.type_names(); it; ++it) {
     protos::pbzero::InternedString::Decoder entry(*it);
     const char* str = reinterpret_cast<const char*>(entry.str().data);
     auto str_view = base::StringView(str, entry.str().size);
 
-    heap_graph_tracker_.AddInternedTypeName(
+    heap_graph_tracker->AddInternedTypeName(
         seq_id, entry.iid(), context_->storage->InternString(str_view));
   }
   for (auto it = heap_graph.field_names(); it; ++it) {
@@ -171,7 +172,7 @@
     const char* str = reinterpret_cast<const char*>(entry.str().data);
     auto str_view = base::StringView(str, entry.str().size);
 
-    heap_graph_tracker_.AddInternedFieldName(
+    heap_graph_tracker->AddInternedFieldName(
         seq_id, entry.iid(), context_->storage->InternString(str_view));
   }
   for (auto it = heap_graph.roots(); it; ++it) {
@@ -191,15 +192,16 @@
           stats::heap_graph_malformed_packet, static_cast<int>(upid));
       break;
     }
-    heap_graph_tracker_.AddRoot(seq_id, upid, ts, std::move(src_root));
+    heap_graph_tracker->AddRoot(seq_id, upid, ts, std::move(src_root));
   }
   if (!heap_graph.continued()) {
-    heap_graph_tracker_.FinalizeProfile(seq_id);
+    heap_graph_tracker->FinalizeProfile(seq_id);
   }
 }
 
 void HeapGraphModule::ParseDeobfuscationMapping(protozero::ConstBytes blob) {
   // TODO(fmayer): Support multiple profiles in the same trace.
+  auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
   protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
       blob.data, blob.size);
   for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
@@ -212,15 +214,25 @@
                     cls.obfuscated_name().ToStdString().c_str());
     } else {
       const std::vector<int64_t>* cls_objects =
-          heap_graph_tracker_.RowsForType(*obfuscated_class_name_id);
+          heap_graph_tracker->RowsForType(*obfuscated_class_name_id);
 
       if (cls_objects) {
-        auto interned_deobfuscated_name =
-            context_->storage->InternString(cls.deobfuscated_name());
         for (int64_t row : *cls_objects) {
+          const base::StringView obfuscated_type_name =
+              context_->storage->GetString(
+                  context_->storage->mutable_heap_graph_object_table()
+                      ->type_name()[static_cast<uint32_t>(row)]);
+          size_t array_count = NumberOfArrays(obfuscated_type_name);
+          std::string arrayed_deobfuscated_name =
+              cls.deobfuscated_name().ToStdString();
+          for (size_t i = 0; i < array_count; ++i)
+            arrayed_deobfuscated_name += "[]";
+          const StringId arrayed_deobfuscated_name_id =
+              context_->storage->InternString(
+                  base::StringView(arrayed_deobfuscated_name));
           context_->storage->mutable_heap_graph_object_table()
               ->mutable_deobfuscated_type_name()
-              ->Set(static_cast<uint32_t>(row), interned_deobfuscated_name);
+              ->Set(static_cast<uint32_t>(row), arrayed_deobfuscated_name_id);
         }
       } else {
         PERFETTO_DLOG("Class %s not found",
@@ -245,7 +257,7 @@
       }
 
       const std::vector<int64_t>* field_references =
-          heap_graph_tracker_.RowsForField(*obfuscated_field_name_id);
+          heap_graph_tracker->RowsForField(*obfuscated_field_name_id);
       if (field_references) {
         auto interned_deobfuscated_name = context_->storage->InternString(
             base::StringView(merged_deobfuscated));
diff --git a/src/trace_processor/importers/proto/heap_graph_module.h b/src/trace_processor/importers/proto/heap_graph_module.h
index 492ba60..2fde80e 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.h
+++ b/src/trace_processor/importers/proto/heap_graph_module.h
@@ -40,7 +40,6 @@
   void ParseDeobfuscationMapping(protozero::ConstBytes);
 
   TraceProcessorContext* context_;
-  HeapGraphTracker heap_graph_tracker_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index 0f9ae9a..e2c1fa2 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -19,6 +19,23 @@
 namespace perfetto {
 namespace trace_processor {
 
+size_t NumberOfArrays(base::StringView type) {
+  if (type.size() < 2)
+    return 0;
+
+  size_t arrays = 0;
+  while (type.size() >= 2 * (arrays + 1) &&
+         memcmp(type.end() - 2 * (arrays + 1), "[]", 2) == 0) {
+    arrays++;
+  }
+
+  return arrays;
+}
+
+base::StringView NormalizeTypeName(base::StringView type) {
+  return base::StringView(type.data(), type.size() - NumberOfArrays(type) * 2);
+}
+
 HeapGraphTracker::HeapGraphTracker(TraceProcessorContext* context)
     : context_(context) {}
 
@@ -87,10 +104,24 @@
 
 void HeapGraphTracker::SetPacketIndex(uint32_t seq_id, uint64_t index) {
   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
-  if (sequence_state.prev_index != 0 &&
-      sequence_state.prev_index + 1 != index) {
-    PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
-                  sequence_state.prev_index, index);
+  bool dropped_packet = false;
+  // perfetto_hprof starts counting at index = 0.
+  if (!sequence_state.prev_index && index != 0) {
+    dropped_packet = true;
+  }
+
+  if (sequence_state.prev_index && *sequence_state.prev_index + 1 != index) {
+    dropped_packet = true;
+  }
+
+  if (dropped_packet) {
+    if (sequence_state.prev_index) {
+      PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
+                    *sequence_state.prev_index, index);
+    } else {
+      PERFETTO_ELOG("Invalid first packet index %" PRIu64 " (!= 0)", index);
+    }
+
     context_->storage->IncrementIndexedStats(
         stats::heap_graph_missing_packet,
         static_cast<int>(sequence_state.current_upid));
@@ -119,9 +150,12 @@
          /*root_type=*/base::nullopt});
     int64_t row = context_->storage->heap_graph_object_table().row_count() - 1;
     sequence_state.object_id_to_row.emplace(obj.object_id, row);
-    class_to_rows_[type_name].emplace_back(row);
+    base::StringView normalized_type =
+        NormalizeTypeName(context_->storage->GetString(type_name));
+    class_to_rows_[context_->storage->InternString(normalized_type)]
+        .emplace_back(row);
     sequence_state.walker.AddNode(row, obj.self_size,
-                                  static_cast<int32_t>(type_name.id));
+                                  static_cast<int32_t>(type_name.raw_id()));
   }
 
   for (const SourceObject& obj : sequence_state.current_objects) {
@@ -187,49 +221,66 @@
     }
   }
 
-  auto* mapping_table =
-      context_->storage->mutable_stack_profile_mapping_table();
-
-  tables::StackProfileMappingTable::Row mapping_row{};
-  mapping_row.name = context_->storage->InternString("JAVA");
-  MappingId mapping_id = mapping_table->Insert(mapping_row);
-
-  uint32_t mapping_idx = *mapping_table->id().IndexOf(mapping_id);
-
   auto paths = sequence_state.walker.FindPathsFromRoot();
-  for (const auto& p : paths.children)
-    WriteFlamegraph(sequence_state, p.second, -1, 0, mapping_idx);
+  walkers_.emplace(
+      std::make_pair(sequence_state.current_upid, sequence_state.current_ts),
+      std::move(sequence_state.walker));
 
   sequence_state_.erase(seq_id);
 }
 
-void HeapGraphTracker::WriteFlamegraph(
-    const SequenceState& sequence_state,
-    const HeapGraphWalker::PathFromRoot& path,
-    int32_t parent_id,
-    uint32_t depth,
-    uint32_t mapping_row) {
-  tables::StackProfileFrameTable::Row row{};
-  row.name = StringId(static_cast<uint32_t>(path.class_name));
-  row.mapping = mapping_row;
+std::unique_ptr<tables::ExperimentalFlamegraphNodesTable>
+HeapGraphTracker::BuildFlamegraph(const int64_t current_ts,
+                                  const UniquePid current_upid) {
+  auto it = walkers_.find(std::make_pair(current_upid, current_ts));
+  if (it == walkers_.end())
+    return nullptr;
 
-  auto id = context_->storage->mutable_stack_profile_frame_table()->Insert(row);
-  int32_t frame_id = static_cast<int32_t>(id.value);
+  std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> tbl(
+      new tables::ExperimentalFlamegraphNodesTable(
+          context_->storage->mutable_string_pool(), nullptr));
 
-  auto* callsites = context_->storage->mutable_stack_profile_callsite_table();
-  auto callsite_id = callsites->Insert({depth, parent_id, frame_id});
-  parent_id = static_cast<int32_t>(callsite_id.value);
-  depth++;
+  HeapGraphWalker::PathFromRoot init_path = it->second.FindPathsFromRoot();
+  auto profile_type = context_->storage->InternString("graph");
+  auto java_mapping = context_->storage->InternString("JAVA");
 
-  tables::HeapProfileAllocationTable::Row alloc_row{
-      sequence_state.current_ts, sequence_state.current_upid, parent_id,
-      static_cast<int64_t>(path.count), static_cast<int64_t>(path.size)};
-  // TODO(fmayer): Maybe add a separate table for heap graph flamegraphs.
-  context_->storage->mutable_heap_profile_allocation_table()->Insert(alloc_row);
-  for (const auto& p : path.children) {
-    const HeapGraphWalker::PathFromRoot& child = p.second;
-    WriteFlamegraph(sequence_state, child, parent_id, depth, mapping_row);
+  std::vector<int32_t> node_to_cumulative_size(init_path.nodes.size());
+  std::vector<int32_t> node_to_cumulative_count(init_path.nodes.size());
+  // i > 0 is to skip the artifical root node.
+  for (size_t i = init_path.nodes.size() - 1; i > 0; --i) {
+    const HeapGraphWalker::PathFromRoot::Node& node = init_path.nodes[i];
+
+    node_to_cumulative_size[i] += node.size;
+    node_to_cumulative_count[i] += node.count;
+    node_to_cumulative_size[node.parent_id] += node_to_cumulative_size[i];
+    node_to_cumulative_count[node.parent_id] += node_to_cumulative_count[i];
   }
+
+  std::vector<uint32_t> node_to_row_idx(init_path.nodes.size());
+  // i = 1 is to skip the artifical root node.
+  for (size_t i = 1; i < init_path.nodes.size(); ++i) {
+    const HeapGraphWalker::PathFromRoot::Node& node = init_path.nodes[i];
+    PERFETTO_CHECK(node.parent_id < i);
+    base::Optional<uint32_t> parent_id;
+    if (node.parent_id != 0)
+      parent_id = node_to_row_idx[node.parent_id];
+    const uint32_t depth = node.depth;
+
+    tables::ExperimentalFlamegraphNodesTable::Row alloc_row{
+        current_ts, current_upid, profile_type, depth,
+        StringId::Raw(static_cast<uint32_t>(node.class_name)), java_mapping,
+        static_cast<int64_t>(node.count),
+        static_cast<int64_t>(node_to_cumulative_count[i]),
+        static_cast<int64_t>(node.size),
+        static_cast<int64_t>(node_to_cumulative_size[i]),
+        // For java dumps, set alloc_count == count, etc.
+        static_cast<int64_t>(node.count),
+        static_cast<int64_t>(node_to_cumulative_count[i]),
+        static_cast<int64_t>(node.size),
+        static_cast<int64_t>(node_to_cumulative_size[i]), parent_id};
+    node_to_row_idx[i] = *tbl->id().IndexOf(tbl->Insert(alloc_row));
+  }
+  return tbl;
 }
 
 void HeapGraphTracker::MarkReachable(int64_t row) {
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.h b/src/trace_processor/importers/proto/heap_graph_tracker.h
index 56c9e7f..0699311 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.h
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.h
@@ -32,7 +32,10 @@
 
 class TraceProcessorContext;
 
-class HeapGraphTracker : public HeapGraphWalker::Delegate {
+size_t NumberOfArrays(base::StringView type);
+base::StringView NormalizeTypeName(base::StringView type);
+
+class HeapGraphTracker : public HeapGraphWalker::Delegate, public Destructible {
  public:
   struct SourceObject {
     // All ids in this are in the trace iid space, not in the trace processor
@@ -54,6 +57,13 @@
 
   explicit HeapGraphTracker(TraceProcessorContext* context);
 
+  static HeapGraphTracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->heap_graph_tracker) {
+      context->heap_graph_tracker.reset(new HeapGraphTracker(context));
+    }
+    return static_cast<HeapGraphTracker*>(context->heap_graph_tracker.get());
+  }
+
   void AddRoot(uint32_t seq_id, UniquePid upid, int64_t ts, SourceRoot root);
   void AddObject(uint32_t seq_id, UniquePid upid, int64_t ts, SourceObject obj);
   void AddInternedTypeName(uint32_t seq_id,
@@ -86,6 +96,10 @@
     return &it->second;
   }
 
+  std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> BuildFlamegraph(
+      const int64_t current_ts,
+      const UniquePid current_upid);
+
  private:
   struct SequenceState {
     SequenceState(HeapGraphTracker* tracker) : walker(tracker) {}
@@ -97,21 +111,17 @@
     std::map<uint64_t, StringPool::Id> interned_type_names;
     std::map<uint64_t, StringPool::Id> interned_field_names;
     std::map<uint64_t, int64_t> object_id_to_row;
-    uint64_t prev_index = 0;
+    base::Optional<uint64_t> prev_index;
     HeapGraphWalker walker;
   };
 
   SequenceState& GetOrCreateSequence(uint32_t seq_id);
   bool SetPidAndTimestamp(SequenceState* seq, UniquePid upid, int64_t ts);
 
-  void WriteFlamegraph(const SequenceState& sequence_state,
-                       const HeapGraphWalker::PathFromRoot& path,
-                       int32_t parent_id,
-                       uint32_t depth,
-                       uint32_t mapping_row);
 
   TraceProcessorContext* const context_;
   std::map<uint32_t, SequenceState> sequence_state_;
+  std::map<std::pair<UniquePid, int64_t /* ts */>, HeapGraphWalker> walkers_;
 
   std::map<StringPool::Id, std::vector<int64_t>> class_to_rows_;
   std::map<StringPool::Id, std::vector<int64_t>> field_to_rows_;
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
new file mode 100644
index 0000000..aa4e64d
--- /dev/null
+++ b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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/trace_processor/importers/proto/heap_graph_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::testing::UnorderedElementsAre;
+
+TEST(HeapGraphTrackerTest, BuildFlamegraph) {
+  //           4@A 5@B
+  //             \ /
+  //         2@Y 3@Y
+  //           \ /
+  //           1@X
+
+  constexpr uint64_t kSeqId = 1;
+  constexpr UniquePid kPid = 1;
+  constexpr int64_t kTimestamp = 1;
+
+  TraceProcessorContext context;
+  context.storage.reset(new TraceStorage());
+
+  HeapGraphTracker tracker(&context);
+
+  constexpr uint64_t kField = 1;
+
+  constexpr uint64_t kX = 1;
+  constexpr uint64_t kY = 2;
+  constexpr uint64_t kA = 3;
+  constexpr uint64_t kB = 4;
+
+  StringPool::Id field = context.storage->InternString("foo");
+  StringPool::Id x = context.storage->InternString("X");
+  StringPool::Id y = context.storage->InternString("Y");
+  StringPool::Id a = context.storage->InternString("A");
+  StringPool::Id b = context.storage->InternString("B");
+
+  tracker.AddInternedFieldName(kSeqId, kField, field);
+
+  tracker.AddInternedTypeName(kSeqId, kX, x);
+  tracker.AddInternedTypeName(kSeqId, kY, y);
+  tracker.AddInternedTypeName(kSeqId, kA, a);
+  tracker.AddInternedTypeName(kSeqId, kB, b);
+
+  {
+    HeapGraphTracker::SourceObject obj;
+    obj.object_id = 1;
+    obj.self_size = 1;
+    obj.type_id = kX;
+    HeapGraphTracker::SourceObject::Reference ref;
+    ref.field_name_id = kField;
+    ref.owned_object_id = 2;
+    obj.references.emplace_back(std::move(ref));
+
+    ref.field_name_id = kField;
+    ref.owned_object_id = 3;
+    obj.references.emplace_back(std::move(ref));
+
+    tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
+  }
+
+  {
+    HeapGraphTracker::SourceObject obj;
+    obj.object_id = 2;
+    obj.self_size = 2;
+    obj.type_id = kY;
+    tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
+  }
+
+  {
+    HeapGraphTracker::SourceObject obj;
+    obj.object_id = 3;
+    obj.self_size = 3;
+    obj.type_id = kY;
+    HeapGraphTracker::SourceObject::Reference ref;
+    ref.field_name_id = kField;
+    ref.owned_object_id = 4;
+    obj.references.emplace_back(std::move(ref));
+
+    ref.field_name_id = kField;
+    ref.owned_object_id = 5;
+    obj.references.emplace_back(std::move(ref));
+
+    tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
+  }
+
+  {
+    HeapGraphTracker::SourceObject obj;
+    obj.object_id = 4;
+    obj.self_size = 4;
+    obj.type_id = kA;
+    tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
+  }
+
+  {
+    HeapGraphTracker::SourceObject obj;
+    obj.object_id = 5;
+    obj.self_size = 5;
+    obj.type_id = kB;
+    tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
+  }
+
+  HeapGraphTracker::SourceRoot root;
+  root.root_type = context.storage->InternString("ROOT");
+  root.object_ids.emplace_back(1);
+  tracker.AddRoot(kSeqId, kPid, kTimestamp, root);
+
+  tracker.FinalizeProfile(kSeqId);
+  std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> flame =
+      tracker.BuildFlamegraph(kPid, kTimestamp);
+  ASSERT_NE(flame, nullptr);
+
+  auto cumulative_sizes = flame->cumulative_size().ToVectorForTesting();
+  EXPECT_THAT(cumulative_sizes, UnorderedElementsAre(15, 4, 14, 5));
+
+  auto cumulative_counts = flame->cumulative_count().ToVectorForTesting();
+  EXPECT_THAT(cumulative_counts, UnorderedElementsAre(5, 4, 1, 1));
+
+  auto sizes = flame->size().ToVectorForTesting();
+  EXPECT_THAT(sizes, UnorderedElementsAre(1, 5, 4, 5));
+
+  auto counts = flame->count().ToVectorForTesting();
+  EXPECT_THAT(counts, UnorderedElementsAre(1, 2, 1, 1));
+}
+
+static const char kArray[] = "X[]";
+static const char kDoubleArray[] = "X[][]";
+static const char kNoArray[] = "X";
+static const char kLongNoArray[] = "ABCDE";
+
+TEST(HeapGraphTrackerTest, NormalizeTypeName) {
+  // sizeof(...) - 1 below to get rid of the null-byte.
+  EXPECT_EQ(NormalizeTypeName(base::StringView(kArray, sizeof(kArray) - 1))
+                .ToStdString(),
+            "X");
+  EXPECT_EQ(NormalizeTypeName(
+                base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1))
+                .ToStdString(),
+            "X");
+  EXPECT_EQ(NormalizeTypeName(base::StringView(kNoArray, sizeof(kNoArray) - 1))
+                .ToStdString(),
+            "X");
+  EXPECT_EQ(NormalizeTypeName(
+                base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1))
+                .ToStdString(),
+            "ABCDE");
+}
+
+TEST(HeapGraphTrackerTest, NumberOfArray) {
+  // sizeof(...) - 1 below to get rid of the null-byte.
+  EXPECT_EQ(NumberOfArrays(base::StringView(kArray, sizeof(kArray) - 1)), 1u);
+  EXPECT_EQ(
+      NumberOfArrays(base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1)),
+      2u);
+  EXPECT_EQ(NumberOfArrays(base::StringView(kNoArray, sizeof(kNoArray) - 1)),
+            0u);
+  EXPECT_EQ(
+      NumberOfArrays(base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1)),
+      0u);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.cc b/src/trace_processor/importers/proto/heap_graph_walker.cc
index 90fd398..c7ba842 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker.cc
@@ -324,27 +324,70 @@
 }
 
 HeapGraphWalker::PathFromRoot HeapGraphWalker::FindPathsFromRoot() {
+  PathFromRoot path;
   for (Node* root : roots_)
-    FindPathFromRoot(root, &path_);
+    FindPathFromRoot(root, &path);
   for (Node& node : nodes_)
     node.find_paths_from_root_visited = false;
-  return std::move(path_);
+  return path;
 }
 
 // TODO(fmayer): Teach this to handle field names.
-void HeapGraphWalker::FindPathFromRoot(Node* n, PathFromRoot* parent) {
-  PathFromRoot& cur = parent->children[n->class_name];
-  cur.size += n->self_size;
-  cur.count++;
-  cur.parent = parent;
-  cur.class_name = n->class_name;
-  for (Node* child : n->children) {
-    if (child->distance_to_root == n->distance_to_root + 1 &&
-        !child->find_paths_from_root_visited) {
-      // Mark as visited in case there is another path with the same distance
-      // from a root.
-      child->find_paths_from_root_visited = true;
-      FindPathFromRoot(child, &cur);
+void HeapGraphWalker::FindPathFromRoot(Node* first_node, PathFromRoot* path) {
+  // We have long retention chains (e.g. from LinkedList). If we use the stack
+  // here, we risk running out of stack space. This is why we use a vector to
+  // simulate the stack.
+  struct StackElem {
+    Node* node;        // Node in the original graph.
+    size_t parent_id;  // id of parent node in the result tree.
+    size_t i;          // Index of the next child of this node to handle.
+    uint32_t depth;    // Depth in the resulting tree
+                       // (including artifical root).
+  };
+
+  std::vector<StackElem> stack{{first_node, PathFromRoot::kRoot, 0, 0}};
+
+  while (!stack.empty()) {
+    Node* n = stack.back().node;
+    size_t parent_id = stack.back().parent_id;
+    uint32_t depth = stack.back().depth;
+    size_t& i = stack.back().i;
+
+    auto it = path->nodes[parent_id].children.find(n->class_name);
+    if (it == path->nodes[parent_id].children.end()) {
+      size_t id = path->nodes.size();
+      path->nodes.emplace_back(PathFromRoot::Node{});
+      std::tie(it, std::ignore) =
+          path->nodes[parent_id].children.emplace(n->class_name, id);
+      path->nodes.back().class_name = n->class_name;
+      path->nodes.back().depth = depth;
+      path->nodes.back().parent_id = parent_id;
+    }
+    size_t id = it->second;
+    PathFromRoot::Node* output_tree_node = &path->nodes[id];
+
+    if (i == 0) {
+      // This is the first time we are looking at this node, so add its
+      // size to the relevant node in the resulting tree.
+      output_tree_node->size += n->self_size;
+      output_tree_node->count++;
+    }
+    // Otherwise we have already handled this node and just need to get its
+    // i-th child.
+    if (!n->children.empty()) {
+      Node* child = n->children[i];
+      if (++i == n->children.size())
+        stack.pop_back();
+
+      if (child->distance_to_root == n->distance_to_root + 1 &&
+          !child->find_paths_from_root_visited) {
+        // Mark as visited in case there is another path with the same distance
+        // from a root.
+        child->find_paths_from_root_visited = true;
+        stack.emplace_back(StackElem{child, id, 0, depth + 1});
+      }
+    } else {
+      stack.pop_back();
     }
   }
 }
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.h b/src/trace_processor/importers/proto/heap_graph_walker.h
index 4ec3dc8..052320b 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.h
+++ b/src/trace_processor/importers/proto/heap_graph_walker.h
@@ -97,12 +97,20 @@
 
 class HeapGraphWalker {
  public:
+  using ClassNameId = int32_t;
+
   struct PathFromRoot {
-    uint64_t size = 0;
-    uint64_t count = 0;
-    int32_t class_name = -1;
-    std::map<int32_t, PathFromRoot> children;
-    PathFromRoot* parent = nullptr;
+    static constexpr size_t kRoot = 0;
+    struct Node {
+      uint32_t depth = 0;
+      // Invariant: parent_id < id of this node.
+      size_t parent_id = 0;
+      uint64_t size = 0;
+      uint64_t count = 0;
+      ClassNameId class_name = -1;
+      std::map<ClassNameId, size_t> children;
+    };
+    std::vector<Node> nodes{Node{}};
   };
 
   class Delegate {
@@ -118,7 +126,7 @@
 
   void AddEdge(int64_t owner_row, int64_t owned_row);
   void AddNode(int64_t row, uint64_t size) { AddNode(row, size, -1); }
-  void AddNode(int64_t row, uint64_t size, int32_t class_name);
+  void AddNode(int64_t row, uint64_t size, ClassNameId class_name);
 
   // Mark a a node as root. This marks all the nodes reachable from it as
   // reachable.
@@ -170,14 +178,13 @@
   void FoundSCC(Node*);
   int64_t RetainedSize(const Component&);
 
-  void FindPathFromRoot(Node* n, PathFromRoot* parent);
+  void FindPathFromRoot(Node* n, PathFromRoot* path);
 
   std::vector<Component> components_;
   std::vector<Node*> node_stack_;
   uint64_t next_node_index_ = 1;
   std::vector<Node> nodes_;
 
-  PathFromRoot path_;
   std::vector<Node*> roots_;
 
   Delegate* delegate_;
diff --git a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
index 4b1b586..9326133 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
@@ -584,14 +584,21 @@
 }
 
 bool HasPath(const HeapGraphWalker::PathFromRoot& path,
-             std::vector<int32_t> class_names) {
+             const HeapGraphWalker::PathFromRoot::Node& node,
+             std::vector<HeapGraphWalker::ClassNameId> class_names) {
   if (class_names.empty())
     return true;
-  auto it = path.children.find(class_names[0]);
-  if (it == path.children.end())
+  auto it = node.children.find(class_names[0]);
+  if (it == node.children.end())
     return false;
   class_names.erase(class_names.begin());
-  return HasPath(it->second, class_names);
+  return HasPath(path, path.nodes[it->second], std::move(class_names));
+}
+
+bool HasPath(const HeapGraphWalker::PathFromRoot& path,
+             std::vector<int32_t> class_names) {
+  return HasPath(path, path.nodes[HeapGraphWalker::PathFromRoot::kRoot],
+                 std::move(class_names));
 }
 
 //    1      |
@@ -601,7 +608,7 @@
 //  ^        |
 //  |        |
 //  4R       |
-TEST(HeapGraphWalkeTest, ShortestPath) {
+TEST(HeapGraphWalkerTest, ShortestPath) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
   walker.AddNode(1, 1, 1);
@@ -630,7 +637,7 @@
 //  ^        |
 //  |        |
 //  4R       |
-TEST(HeapGraphWalkeTest, ShortestPathMultipleRoots) {
+TEST(HeapGraphWalkerTest, ShortestPathMultipleRoots) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
   walker.AddNode(1, 1, 1);
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index 903f99d..81ec6e0 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -47,7 +47,7 @@
  public:
   InternedMessageView(TraceBlobView msg) : message_(std::move(msg)) {}
 
-  InternedMessageView(InternedMessageView&&) noexcept = default;
+  InternedMessageView(InternedMessageView&&) = default;
   InternedMessageView& operator=(InternedMessageView&&) = default;
 
   // Allow copy by cloning the TraceBlobView. This is required for
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 9fe74c8..5b36057 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -43,7 +43,7 @@
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/track_tracker.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/variadic.h"
 
 #include "protos/perfetto/common/trace_stats.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
@@ -174,7 +174,8 @@
           context->storage->InternString("chrome_event.legacy_user_trace")) {
   // TODO(140860736): Once we support null values for
   // stack_profile_frame.symbol_set_id remove this hack
-  context_->storage->mutable_symbol_table()->Insert({0, 0, 0, 0});
+  context_->storage->mutable_symbol_table()->Insert(
+      {0, kNullStringId, kNullStringId, 0});
 }
 
 ProtoTraceParser::~ProtoTraceParser() = default;
@@ -381,6 +382,9 @@
 
     int pid = static_cast<int>(entry.pid());
 
+    if (entry.disconnected())
+      context_->storage->IncrementIndexedStats(
+          stats::heapprofd_client_disconnected, pid);
     if (entry.buffer_corrupted())
       context_->storage->IncrementIndexedStats(
           stats::heapprofd_buffer_corrupted, pid);
@@ -444,18 +448,19 @@
       break;
     }
 
-    auto maybe_callstack_id =
-        stack_profile_tracker.FindCallstack(*callstack_it, &intern_lookup);
+    auto maybe_callstack_id = stack_profile_tracker.FindOrInsertCallstack(
+        *callstack_it, &intern_lookup);
     if (!maybe_callstack_id) {
       context_->storage->IncrementStats(stats::stackprofile_parser_error);
       PERFETTO_ELOG("StreamingProfilePacket referencing invalid callstack!");
       continue;
     }
 
-    int64_t callstack_id = *maybe_callstack_id;
+    uint32_t callstack_id = maybe_callstack_id->value;
 
     tables::CpuProfileStackSampleTable::Row sample_row{
-        sequence_state->state()->IncrementAndGetTrackEventTimeNs(*timestamp_it),
+        sequence_state->state()->IncrementAndGetTrackEventTimeNs(*timestamp_it *
+                                                                 1000),
         callstack_id, utid};
     storage->mutable_cpu_profile_stack_sample_table()->Insert(sample_row);
   }
@@ -515,8 +520,9 @@
   protos::pbzero::ChromeEventBundle::Decoder bundle(blob.data, blob.size);
   ArgsTracker args(context_);
   if (bundle.has_metadata()) {
-    uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-        ts, raw_chrome_metadata_event_id_, 0, 0);
+    RawId id = storage->mutable_raw_table()->Insert(
+        {ts, raw_chrome_metadata_event_id_, 0, 0});
+    auto inserter = args.AddArgsTo(id);
 
     // Metadata is proxied via a special event in the raw table to JSON export.
     for (auto it = bundle.metadata(); it; ++it) {
@@ -534,14 +540,15 @@
         value = Variadic::Json(storage->InternString(metadata.json_value()));
       } else {
         context_->storage->IncrementStats(stats::empty_chrome_metadata);
+        continue;
       }
-      args.AddArg(TableId::kRawEvents, row, name_id, name_id, value);
+      args.AddArgsTo(id).AddArg(name_id, value);
     }
   }
 
   if (bundle.has_legacy_ftrace_output()) {
-    uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-        ts, raw_chrome_legacy_system_trace_event_id_, 0, 0);
+    RawId id = storage->mutable_raw_table()->Insert(
+        {ts, raw_chrome_legacy_system_trace_event_id_, 0, 0});
 
     std::string data;
     for (auto it = bundle.legacy_ftrace_output(); it; ++it) {
@@ -549,7 +556,7 @@
     }
     Variadic value =
         Variadic::String(storage->InternString(base::StringView(data)));
-    args.AddArg(TableId::kRawEvents, row, data_name_id_, data_name_id_, value);
+    args.AddArgsTo(id).AddArg(data_name_id_, value);
   }
 
   if (bundle.has_legacy_json_trace()) {
@@ -559,12 +566,11 @@
           protos::pbzero::ChromeLegacyJsonTrace::USER_TRACE) {
         continue;
       }
-      uint32_t row = storage->mutable_raw_events()->AddRawEvent(
-          ts, raw_chrome_legacy_user_trace_event_id_, 0, 0);
+      RawId id = storage->mutable_raw_table()->Insert(
+          {ts, raw_chrome_legacy_user_trace_event_id_, 0, 0});
       Variadic value =
           Variadic::String(storage->InternString(legacy_trace.data()));
-      args.AddArg(TableId::kRawEvents, row, data_name_id_, data_name_id_,
-                  value);
+      args.AddArgsTo(id).AddArg(data_name_id_, value);
     }
   }
 }
@@ -574,7 +580,7 @@
   auto utid = context_->process_tracker->GetOrCreateThread(event.thread_id());
 
   StringId cat_id = metatrace_id_;
-  StringId name_id = 0;
+  StringId name_id = kNullStringId;
   char fallback[64];
 
   if (event.has_event_id()) {
@@ -626,12 +632,19 @@
 
 void ProtoTraceParser::ParseModuleSymbols(ConstBytes blob) {
   protos::pbzero::ModuleSymbols::Decoder module_symbols(blob.data, blob.size);
-  std::string hex_build_id = base::ToHex(module_symbols.build_id().data,
-                                         module_symbols.build_id().size);
-  auto mapping_rows = context_->storage->FindMappingRow(
-      context_->storage->InternString(module_symbols.path()),
-      context_->storage->InternString(base::StringView(hex_build_id)));
-  if (mapping_rows.empty()) {
+  StringId build_id;
+  // TODO(b/148109467): Remove workaround once all active Chrome versions
+  // write raw bytes instead of a string as build_id.
+  if (module_symbols.build_id().size == 33) {
+    build_id = context_->storage->InternString(module_symbols.build_id());
+  } else {
+    build_id = context_->storage->InternString(base::StringView(base::ToHex(
+        module_symbols.build_id().data, module_symbols.build_id().size)));
+  }
+
+  auto mapping_ids = context_->storage->FindMappingRow(
+      context_->storage->InternString(module_symbols.path()), build_id);
+  if (mapping_ids.empty()) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
     return;
   }
@@ -640,16 +653,14 @@
 
     uint32_t symbol_set_id = context_->storage->symbol_table().row_count();
     bool frame_found = false;
-    for (int64_t mapping_row : mapping_rows) {
-      std::vector<int64_t> frame_rows = context_->storage->FindFrameRow(
-          static_cast<size_t>(mapping_row), address_symbols.address());
+    for (MappingId mapping_id : mapping_ids) {
+      std::vector<FrameId> frame_ids = context_->storage->FindFrameIds(
+          mapping_id, address_symbols.address());
 
-      for (const int64_t frame_row : frame_rows) {
-        PERFETTO_DCHECK(frame_row >= 0);
-
-        uint32_t row_idx = static_cast<uint32_t>(frame_row);
+      for (const FrameId frame_id : frame_ids) {
         auto* frames = context_->storage->mutable_stack_profile_frame_table();
-        frames->mutable_symbol_set_id()->Set(row_idx, symbol_set_id);
+        uint32_t frame_row = *frames->id().IndexOf(frame_id);
+        frames->mutable_symbol_set_id()->Set(frame_row, symbol_set_id);
         frame_found = true;
       }
     }
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index f90418d..d490f9a 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -58,6 +58,7 @@
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/log_message.pbzero.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "protos/perfetto/trace/track_event/task_execution.pbzero.h"
 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
@@ -79,6 +80,7 @@
 using ::testing::NiceMock;
 using ::testing::Pointwise;
 using ::testing::Return;
+using ::testing::ReturnRef;
 using ::testing::UnorderedElementsAreArray;
 
 namespace {
@@ -149,42 +151,20 @@
   MOCK_METHOD2(UpdateThread, UniqueTid(uint32_t tid, uint32_t tgid));
 
   MOCK_METHOD1(GetOrCreateProcess, UniquePid(uint32_t pid));
-};
-
-// Mock trace storage that behaves like the real implementation, but allows for
-// the interactions with string interning/lookup to be overridden/inspected.
-class MockTraceStorage : public TraceStorage {
- public:
-  MockTraceStorage() : TraceStorage() {
-    ON_CALL(*this, InternString(_))
-        .WillByDefault(Invoke([this](base::StringView str) {
-          return TraceStorage::InternString(str);
-        }));
-
-    ON_CALL(*this, GetString(_)).WillByDefault(Invoke([this](StringId id) {
-      return TraceStorage::GetString(id);
-    }));
-
-    ON_CALL(*this, GetThread(_))
-        .WillByDefault(Invoke(this, &MockTraceStorage::GetThreadImpl));
-  }
-
-  const Thread& GetThreadImpl(UniqueTid utid) {
-    return TraceStorage::GetThread(utid);
-  }
-
-  MOCK_METHOD1(InternString, StringId(base::StringView));
-  MOCK_CONST_METHOD1(GetString, NullTermStringView(StringId));
-
-  MOCK_CONST_METHOD1(GetThread, const Thread&(UniqueTid));
+  MOCK_METHOD2(SetProcessNameIfUnset,
+               void(UniquePid upid, StringId process_name_id));
 };
 
 class MockBoundInserter : public ArgsTracker::BoundInserter {
  public:
-  MockBoundInserter()
-      : ArgsTracker::BoundInserter(nullptr, TableId::kSched, 0u) {}
+  MockBoundInserter() : ArgsTracker::BoundInserter(nullptr, nullptr, 0u) {
+    ON_CALL(*this, AddArg(_, _, _)).WillByDefault(ReturnRef(*this));
+  }
 
-  MOCK_METHOD3(AddArg, void(StringId flat_key, StringId key, Variadic v));
+  MOCK_METHOD3(AddArg,
+               ArgsTracker::BoundInserter&(StringId flat_key,
+                                           StringId key,
+                                           Variadic v));
 };
 
 class MockSliceTracker : public SliceTracker {
@@ -215,9 +195,10 @@
 class ProtoTraceParserTest : public ::testing::Test {
  public:
   ProtoTraceParserTest() {
-    storage_ = new NiceMock<MockTraceStorage>();
+    storage_ = new TraceStorage();
     context_.storage.reset(storage_);
     context_.track_tracker.reset(new TrackTracker(&context_));
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.metadata_tracker.reset(new MetadataTracker(&context_));
     event_ = new MockEventTracker(&context_);
@@ -260,22 +241,19 @@
   }
 
   bool HasArg(ArgSetId set_id, StringId key_id, Variadic value) {
-    const auto& args = storage_->args();
-    auto rows =
-        std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
-    for (; rows.first != rows.second; rows.first++) {
-      size_t index = static_cast<size_t>(
-          std::distance(args.set_ids().begin(), rows.first));
-      if (args.keys()[index] == key_id) {
-        EXPECT_EQ(args.flat_keys()[index], key_id);
-        EXPECT_EQ(args.arg_values()[index], value);
-        if (args.flat_keys()[index] == key_id &&
-            args.arg_values()[index] == value) {
-          return true;
+    const auto& args = storage_->arg_table();
+    RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
+    bool found = false;
+    for (auto it = rm.IterateRows(); it; it.Next()) {
+      if (args.key()[it.row()] == key_id) {
+        EXPECT_EQ(args.flat_key()[it.row()], key_id);
+        if (storage_->GetArgValue(it.row()) == value) {
+          found = true;
+          break;
         }
       }
     }
-    return false;
+    return found;
   }
 
  protected:
@@ -288,7 +266,7 @@
   MockProcessTracker* process_;
   MockSliceTracker* slice_;
   ClockTracker* clock_;
-  NiceMock<MockTraceStorage>* storage_;
+  TraceStorage* storage_;
 };
 
 // TODO(eseckler): Refactor these into a new file for ftrace tests.
@@ -344,24 +322,22 @@
   static const char buf_value[] = "This is a print event";
   print->set_buf(buf_value);
 
-  EXPECT_CALL(*process_, UpdateThread(123, 123));
+  EXPECT_CALL(*process_, GetOrCreateProcess(123));
 
   Tokenize();
 
-  const auto& raw = context_.storage->raw_events();
-  ASSERT_EQ(raw.raw_event_count(), 2u);
-  const auto& args = context_.storage->args();
-  ASSERT_EQ(args.args_count(), 6u);
-  ASSERT_EQ(args.arg_values()[0].int_value, 123);
-  ASSERT_STREQ(
-      context_.storage->GetString(args.arg_values()[1].string_value).c_str(),
-      task_newtask);
-  ASSERT_EQ(args.arg_values()[2].int_value, 12);
-  ASSERT_EQ(args.arg_values()[3].int_value, 15);
-  ASSERT_EQ(args.arg_values()[4].int_value, 20);
-  ASSERT_STREQ(
-      context_.storage->GetString(args.arg_values()[5].string_value).c_str(),
-      buf_value);
+  const auto& raw = context_.storage->raw_table();
+  ASSERT_EQ(raw.row_count(), 2u);
+  const auto& args = context_.storage->arg_table();
+  ASSERT_EQ(args.row_count(), 6u);
+  ASSERT_EQ(args.int_value()[0], 123);
+  ASSERT_STREQ(context_.storage->GetString(args.string_value()[1]).c_str(),
+               task_newtask);
+  ASSERT_EQ(args.int_value()[2], 12);
+  ASSERT_EQ(args.int_value()[3], 15);
+  ASSERT_EQ(args.int_value()[4], 20);
+  ASSERT_STREQ(context_.storage->GetString(args.string_value()[5]).c_str(),
+               buf_value);
 
   // TODO(taylori): Add test ftrace event with all field types
   // and test here.
@@ -393,33 +369,31 @@
   field->set_name("meta3");
   field->set_uint_value(3);
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("Test")));
-  EXPECT_CALL(*storage_, InternString(base::StringView("meta1")));
-  EXPECT_CALL(*storage_, InternString(base::StringView("value1")));
-  EXPECT_CALL(*storage_, InternString(base::StringView("meta2")));
-  EXPECT_CALL(*storage_, InternString(base::StringView("meta3")));
-
   Tokenize();
 
-  const auto& raw = storage_->raw_events();
+  const auto& raw = storage_->raw_table();
 
-  ASSERT_EQ(raw.raw_event_count(), 1u);
-  ASSERT_EQ(raw.timestamps().back(), 100);
-  ASSERT_EQ(storage_->GetThread(raw.utids().back()).tid, 10u);
+  ASSERT_EQ(raw.row_count(), 1u);
+  ASSERT_EQ(raw.ts()[raw.row_count() - 1], 100);
+  ASSERT_EQ(storage_->thread_table().tid()[raw.utid()[raw.row_count() - 1]],
+            10u);
+  ASSERT_EQ(raw.name().GetString(raw.row_count() - 1), "Test");
 
-  auto set_id = raw.arg_set_ids().back();
+  auto set_id = raw.arg_set_id()[raw.row_count() - 1];
 
-  const auto& args = storage_->args();
-  auto id_it =
-      std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
+  const auto& args = storage_->arg_table();
+  RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
 
-  // Ignore string calls as they are handled by checking InternString calls
-  // above.
+  auto row = rm.Get(0);
 
-  auto it = id_it.first;
-  auto row = static_cast<size_t>(std::distance(args.set_ids().begin(), it));
-  ASSERT_EQ(args.arg_values()[++row].int_value, -2);
-  ASSERT_EQ(args.arg_values()[++row].int_value, 3);
+  ASSERT_EQ(args.key().GetString(row), "meta1");
+  ASSERT_EQ(args.string_value().GetString(row++), "value1");
+
+  ASSERT_EQ(args.key().GetString(row), "meta2");
+  ASSERT_EQ(args.int_value()[row++], -2);
+
+  ASSERT_EQ(args.key().GetString(row), "meta3");
+  ASSERT_EQ(args.int_value()[row++], 3);
 }
 
 TEST_F(ProtoTraceParserTest, LoadMultipleEvents) {
@@ -641,6 +615,51 @@
   Tokenize();
 }
 
+TEST_F(ProtoTraceParserTest, ProcessNameFromProcessDescriptor) {
+  context_.sorter.reset(new TraceSorter(
+      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+  {
+    auto* packet = trace_.add_packet();
+    packet->set_trusted_packet_sequence_id(1);
+    packet->set_incremental_state_cleared(true);
+    auto* process_desc = packet->set_process_descriptor();
+    process_desc->set_pid(15);
+    process_desc->set_process_name("OldProcessName");
+  }
+  {
+    auto* packet = trace_.add_packet();
+    packet->set_trusted_packet_sequence_id(1);
+    packet->set_incremental_state_cleared(true);
+    auto* process_desc = packet->set_process_descriptor();
+    process_desc->set_pid(15);
+    process_desc->set_process_name("NewProcessName");
+  }
+  {
+    auto* packet = trace_.add_packet();
+    packet->set_trusted_packet_sequence_id(2);
+    packet->set_incremental_state_cleared(true);
+    auto* process_desc = packet->set_process_descriptor();
+    process_desc->set_pid(16);
+    process_desc->set_process_name("DifferentProcessName");
+  }
+
+  EXPECT_CALL(*process_, GetOrCreateProcess(15))
+      .WillRepeatedly(testing::Return(1u));
+  EXPECT_CALL(*process_, GetOrCreateProcess(16)).WillOnce(testing::Return(2u));
+
+  EXPECT_CALL(*process_, SetProcessNameIfUnset(
+                             1u, storage_->InternString("OldProcessName")));
+  // Packet with same thread, but different name should update the name.
+  EXPECT_CALL(*process_, SetProcessNameIfUnset(
+                             1u, storage_->InternString("NewProcessName")));
+  EXPECT_CALL(*process_,
+              SetProcessNameIfUnset(
+                  2u, storage_->InternString("DifferentProcessName")));
+
+  Tokenize();
+  context_.sorter->ExtractEventsForced();
+}
+
 TEST_F(ProtoTraceParserTest, ThreadNameFromThreadDescriptor) {
   context_.sorter.reset(new TraceSorter(
       &context_, std::numeric_limits<int64_t>::max() /*window size*/));
@@ -682,16 +701,14 @@
       .WillRepeatedly(testing::Return(1u));
   EXPECT_CALL(*process_, UpdateThread(11, 15)).WillOnce(testing::Return(2u));
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("OldThreadName")))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*process_, SetThreadNameIfUnset(1u, StringId(1)));
+  EXPECT_CALL(*process_, SetThreadNameIfUnset(
+                             1u, storage_->InternString("OldThreadName")));
   // Packet with same thread, but different name should update the name.
-  EXPECT_CALL(*storage_, InternString(base::StringView("NewThreadName")))
-      .WillOnce(Return(2));
-  EXPECT_CALL(*process_, SetThreadNameIfUnset(1u, StringId(2)));
-  EXPECT_CALL(*storage_, InternString(base::StringView("DifferentThreadName")))
-      .WillOnce(Return(3));
-  EXPECT_CALL(*process_, SetThreadNameIfUnset(2u, StringId(3)));
+  EXPECT_CALL(*process_, SetThreadNameIfUnset(
+                             1u, storage_->InternString("NewThreadName")));
+  EXPECT_CALL(
+      *process_,
+      SetThreadNameIfUnset(2u, storage_->InternString("DifferentThreadName")));
 
   Tokenize();
   context_.sorter->ExtractEventsForced();
@@ -751,14 +768,11 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(3)
       .WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .Times(3)
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
   MockBoundInserter inserter;
 
@@ -834,14 +848,11 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(3)
       .WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .Times(3)
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
   MockBoundInserter inserter;
 
@@ -972,53 +983,42 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(5)
       .WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 2u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 2u;
+  storage_->mutable_thread_table()->Insert(row);
 
   constexpr TrackId thread_1_track{0u};
   constexpr TrackId process_2_track{1u};
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat2,cat3")))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev2")))
-      .WillOnce(Return(2));
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillRepeatedly(Return(3));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillRepeatedly(Return(4));
+  StringId cat_2_3 = storage_->InternString("cat2,cat3");
+  StringId ev_2 = storage_->InternString("ev2");
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
 
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
   MockBoundInserter inserter;
-  EXPECT_CALL(*slice_, Scoped(1005000, thread_1_track, StringId(1), StringId(2),
-                              23000, _))
+  EXPECT_CALL(*slice_, Scoped(1005000, thread_1_track, cat_2_3, ev_2, 23000, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(0u)));
   EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(9999u)));
   EXPECT_CALL(inserter, AddArg(_, _, Variadic::Boolean(true)));
   EXPECT_CALL(inserter, AddArg(_, _, _));
 
-  EXPECT_CALL(*slice_,
-              Begin(1010000, thread_1_track, StringId(3), StringId(4), _))
+  EXPECT_CALL(*slice_, Begin(1010000, thread_1_track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
 
-  EXPECT_CALL(*slice_,
-              End(1020000, thread_1_track, StringId(3), StringId(4), _))
+  EXPECT_CALL(*slice_, End(1020000, thread_1_track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
 
-  EXPECT_CALL(*slice_,
-              Scoped(1040000, thread_1_track, StringId(3), StringId(4), 0, _))
+  EXPECT_CALL(*slice_, Scoped(1040000, thread_1_track, cat_1, ev_1, 0, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(2u)));
 
-  EXPECT_CALL(*slice_,
-              Scoped(1050000, process_2_track, StringId(3), StringId(4), 0, _))
+  EXPECT_CALL(*slice_, Scoped(1050000, process_2_track, cat_1, ev_1, 0, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(3u)));
-  // Second slice should have a legacy_event.original_tid arg.
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::Integer(16)));
+  // Second slice should have a legacy_event.passthrough_utid arg.
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(1u)));
 
   context_.sorter->ExtractEventsForced();
 
@@ -1141,44 +1141,32 @@
 
   EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillRepeatedly(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillRepeatedly(Return(2));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev2")))
-      .WillRepeatedly(Return(4));
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat2")))
-      .WillRepeatedly(Return(3));
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat2:scope1")))
-      .WillOnce(Return(5));
-  EXPECT_CALL(*storage_, GetString(StringId(1))).WillRepeatedly(Return("cat1"));
-  EXPECT_CALL(*storage_, GetString(StringId(3))).WillRepeatedly(Return("cat2"));
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
+  StringId cat_2 = storage_->InternString("cat2");
+  StringId ev_2 = storage_->InternString("ev2");
 
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
-  EXPECT_CALL(*slice_, Begin(1010000, TrackId{1}, StringId(1), StringId(2), _))
+  EXPECT_CALL(*slice_, Begin(1010000, TrackId{1}, cat_1, ev_1, _))
       .WillOnce(Return(0u));
-  EXPECT_CALL(*slice_,
-              Scoped(1015000, TrackId{1}, StringId(1), StringId(4), 0, _));
-  EXPECT_CALL(*slice_,
-              Scoped(1018000, TrackId{2}, StringId(3), StringId(4), 0, _));
-  EXPECT_CALL(*slice_, End(1020000, TrackId{1}, StringId(1), StringId(2), _))
+  EXPECT_CALL(*slice_, Scoped(1015000, TrackId{1}, cat_1, ev_2, 0, _));
+  EXPECT_CALL(*slice_, Scoped(1018000, TrackId{2}, cat_2, ev_2, 0, _));
+  EXPECT_CALL(*slice_, End(1020000, TrackId{1}, cat_1, ev_1, _))
       .WillOnce(Return(0u));
-  EXPECT_CALL(*slice_,
-              Scoped(1030000, TrackId{3}, StringId(3), StringId(4), 0, _));
+  EXPECT_CALL(*slice_, Scoped(1030000, TrackId{3}, cat_2, ev_2, 0, _));
 
   context_.sorter->ExtractEventsForced();
 
   // First track is for the thread; others are the async event tracks.
   EXPECT_EQ(storage_->track_table().row_count(), 4u);
-  EXPECT_EQ(storage_->track_table().name()[1], 2u);
-  EXPECT_EQ(storage_->track_table().name()[2], 4u);
-  EXPECT_EQ(storage_->track_table().name()[3], 4u);
+  EXPECT_EQ(storage_->track_table().name()[1], ev_1);
+  EXPECT_EQ(storage_->track_table().name()[2], ev_2);
+  EXPECT_EQ(storage_->track_table().name()[3], ev_2);
 
   EXPECT_EQ(storage_->process_track_table().row_count(), 3u);
   EXPECT_EQ(storage_->process_track_table().upid()[0], 1u);
@@ -1205,6 +1193,7 @@
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     packet->set_incremental_state_cleared(true);
+    packet->set_timestamp(1000000);
     auto* track_desc = packet->set_track_descriptor();
     track_desc->set_uuid(1234);
     track_desc->set_name("Thread track 1");
@@ -1215,6 +1204,7 @@
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
+    packet->set_timestamp(1000000);
     auto* track_desc = packet->set_track_descriptor();
     track_desc->set_uuid(5678);
     track_desc->set_name("Async track 1");
@@ -1268,6 +1258,7 @@
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(2);
     packet->set_incremental_state_cleared(true);
+    packet->set_timestamp(1000000);
     auto* track_desc = packet->set_track_descriptor();
     track_desc->set_uuid(4321);
     track_desc->set_name("Thread track 2");
@@ -1276,12 +1267,6 @@
     thread_desc->set_tid(17);
   }
   {
-    auto* packet = trace_.add_packet();
-    packet->set_trusted_packet_sequence_id(2);
-    auto* track_desc = packet->set_track_descriptor();
-    track_desc->set_uuid(5678);  // "Async track 1" defined on sequence 1.
-  }
-  {
     // Async event completed on "Async track 1".
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(2);
@@ -1318,69 +1303,50 @@
   EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
   EXPECT_CALL(*process_, UpdateThread(17, 15)).WillRepeatedly(Return(2));
 
-  TraceStorage::Thread thread1(16);
-  thread1.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .WillRepeatedly(testing::ReturnRef(thread1));
-  TraceStorage::Thread thread2(16);
-  thread2.upid = 2u;
-  EXPECT_CALL(*storage_, GetThread(2))
-      .WillRepeatedly(testing::ReturnRef(thread2));
+  tables::ThreadTable::Row t1(16);
+  t1.upid = 1u;
+  storage_->mutable_thread_table()->Insert(t1);
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("Thread track 1")))
-      .WillOnce(Return(10));
-  EXPECT_CALL(*storage_, InternString(base::StringView("Async track 1")))
-      .WillOnce(Return(11));
-  EXPECT_CALL(*storage_, InternString(base::StringView("Thread track 2")))
-      .WillOnce(Return(12));
-  EXPECT_CALL(*storage_, InternString(base::StringView("")))
-      .WillOnce(Return(0));
+  tables::ThreadTable::Row t2(16);
+  t2.upid = 2u;
+  storage_->mutable_thread_table()->Insert(t2);
 
   Tokenize();
 
-  // First track is "Thread track 1"; second is "Async track 1", third is
-  // "Thread track 2".
-  EXPECT_EQ(storage_->track_table().row_count(), 3u);
-  EXPECT_EQ(storage_->track_table().name()[0], 10u);  // "Thread track 1"
-  EXPECT_EQ(storage_->track_table().name()[1], 11u);  // "Async track 1"
-  EXPECT_EQ(storage_->track_table().name()[2], 12u);  // "Thread track 2"
-  EXPECT_EQ(storage_->thread_track_table().row_count(), 2u);
-  EXPECT_EQ(storage_->thread_track_table().utid()[0], 1u);
-  EXPECT_EQ(storage_->thread_track_table().utid()[1], 2u);
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
+  StringId cat_2 = storage_->InternString("cat2");
+  StringId ev_2 = storage_->InternString("ev2");
+  StringId cat_3 = storage_->InternString("cat3");
+  StringId ev_3 = storage_->InternString("ev3");
 
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillOnce(Return(2));
-  EXPECT_CALL(*slice_, Begin(1010000, TrackId{1}, StringId(1), StringId(2), _))
+  EXPECT_CALL(*slice_, Begin(1010000, TrackId{1}, cat_1, ev_1, _))
       .WillOnce(Return(0u));
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat2")))
-      .WillOnce(Return(3));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev2")))
-      .WillOnce(Return(4));
-  EXPECT_CALL(*slice_,
-              Scoped(1015000, TrackId{0}, StringId(3), StringId(4), 0, _))
+  EXPECT_CALL(*slice_, Scoped(1015000, TrackId{0}, cat_2, ev_2, 0, _))
       .WillOnce(Return(1u));
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat3")))
-      .WillOnce(Return(5));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev3")))
-      .WillOnce(Return(6));
-  EXPECT_CALL(*slice_,
-              Scoped(1016000, TrackId{2}, StringId(5), StringId(6), 0, _))
+  EXPECT_CALL(*slice_, Scoped(1016000, TrackId{3}, cat_3, ev_3, 0, _))
       .WillOnce(Return(2u));
 
-  EXPECT_CALL(*slice_, End(1020000, TrackId{1}, StringId(0), StringId(0), _))
+  EXPECT_CALL(*slice_,
+              End(1020000, TrackId{1}, kNullStringId, kNullStringId, _))
       .WillOnce(Return(0u));
 
   context_.sorter->ExtractEventsForced();
 
-  // Track tables shouldn't have changed.
-  EXPECT_EQ(storage_->track_table().row_count(), 3u);
+  // First track is "Thread track 1"; second is "Async track 1", third is global
+  // default track (parent of async track), fourth is "Thread track 2".
+  EXPECT_EQ(storage_->track_table().row_count(), 4u);
+  EXPECT_EQ(storage_->track_table().name().GetString(0), "Thread track 1");
+  EXPECT_EQ(storage_->track_table().name().GetString(1), "Async track 1");
+  EXPECT_EQ(storage_->track_table().name().GetString(2), "Default Track");
+  EXPECT_EQ(storage_->track_table().name().GetString(3), "Thread track 2");
   EXPECT_EQ(storage_->thread_track_table().row_count(), 2u);
+  EXPECT_EQ(storage_->thread_track_table().utid()[0], 1u);
+  EXPECT_EQ(storage_->thread_track_table().utid()[1], 2u);
 
   EXPECT_EQ(storage_->virtual_track_slices().slice_count(), 1u);
   EXPECT_EQ(storage_->virtual_track_slices().slice_ids()[0], 0u);
@@ -1547,14 +1513,11 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(2)
       .WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .Times(2)
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
   constexpr TrackId track{0u};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
@@ -1652,43 +1615,30 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(2)
       .WillRepeatedly(Return(1));
   EXPECT_CALL(*process_, UpdateThread(17, 15))
-      .Times(2)
       .WillRepeatedly(Return(2));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .Times(2)
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row t1(16);
+  t1.upid = 1u;
+  storage_->mutable_thread_table()->Insert(t1);
 
-  TraceStorage::Thread thread2(17);
-  thread2.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(2))
-      .Times(2)
-      .WillRepeatedly(testing::ReturnRef(thread2));
+  tables::ThreadTable::Row t2(17);
+  t2.upid = 1u;
+  storage_->mutable_thread_table()->Insert(t2);
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillRepeatedly(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev2")))
-      .WillRepeatedly(Return(2));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillRepeatedly(Return(3));
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_2 = storage_->InternString("ev2");
+  StringId ev_1 = storage_->InternString("ev1");
 
   constexpr TrackId thread_2_track{0u};
   constexpr TrackId thread_1_track{1u};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
-  EXPECT_CALL(*slice_,
-              Begin(1005000, thread_2_track, StringId(1), StringId(2), _));
-  EXPECT_CALL(*slice_,
-              Begin(1010000, thread_1_track, StringId(1), StringId(3), _));
-  EXPECT_CALL(*slice_,
-              End(1015000, thread_2_track, StringId(1), StringId(2), _));
-  EXPECT_CALL(*slice_,
-              End(1020000, thread_1_track, StringId(1), StringId(3), _));
+  EXPECT_CALL(*slice_, Begin(1005000, thread_2_track, cat_1, ev_2, _));
+  EXPECT_CALL(*slice_, Begin(1010000, thread_1_track, cat_1, ev_1, _));
+  EXPECT_CALL(*slice_, End(1015000, thread_2_track, cat_1, ev_2, _));
+  EXPECT_CALL(*slice_, End(1020000, thread_1_track, cat_1, ev_1, _));
 
   context_.sorter->ExtractEventsForced();
 }
@@ -1820,91 +1770,64 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(2)
       .WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1))
-      .Times(2)
-      .WillRepeatedly(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillRepeatedly(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillRepeatedly(Return(2));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an1")))
-      .WillOnce(Return(3));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an2")))
-      .WillOnce(Return(4));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an2.child1")))
-      .WillRepeatedly(Return(5));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an2.child2")))
-      .WillRepeatedly(Return(6));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an2.child2[0]")))
-      .WillOnce(Return(7));
-  EXPECT_CALL(*storage_, InternString(base::StringView("child21")))
-      .WillOnce(Return(8));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an2.child2[1]")))
-      .WillOnce(Return(9));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an2.child2[2]")))
-      .WillOnce(Return(10));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an3")))
-      .WillOnce(Return(11));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an4")))
-      .WillOnce(Return(12));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an5")))
-      .WillOnce(Return(13));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an6")))
-      .WillOnce(Return(14));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an7")))
-      .WillOnce(Return(15));
-  EXPECT_CALL(*storage_, InternString(base::StringView("val7")))
-      .WillOnce(Return(16));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an8")))
-      .WillOnce(Return(17));
-  EXPECT_CALL(*storage_, InternString(base::StringView("val8")))
-      .WillOnce(Return(18));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an8_foo")))
-      .WillOnce(Return(19));
-
-  EXPECT_CALL(*storage_, GetString(StringId(4))).WillOnce(Return("debug.an2"));
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
+  StringId debug_an_1 = storage_->InternString("debug.an1");
+  StringId debug_an_2_child_1 = storage_->InternString("debug.an2.child1");
+  StringId debug_an_2_child_2 = storage_->InternString("debug.an2.child2");
+  StringId debug_an_2_child_2_0 = storage_->InternString("debug.an2.child2[0]");
+  StringId child21 = storage_->InternString("child21");
+  StringId debug_an_2_child_2_1 = storage_->InternString("debug.an2.child2[1]");
+  StringId debug_an_2_child_2_2 = storage_->InternString("debug.an2.child2[2]");
+  StringId debug_an_3 = storage_->InternString("debug.an3");
+  StringId debug_an_4 = storage_->InternString("debug.an4");
+  StringId debug_an_5 = storage_->InternString("debug.an5");
+  StringId debug_an_6 = storage_->InternString("debug.an6");
+  StringId debug_an_7 = storage_->InternString("debug.an7");
+  StringId val_7 = storage_->InternString("val7");
+  StringId debug_an_8 = storage_->InternString("debug.an8");
+  StringId val_8 = storage_->InternString("val8");
+  StringId debug_an_8_foo = storage_->InternString("debug.an8_foo");
 
   constexpr TrackId track{0u};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
-  EXPECT_CALL(*slice_, Begin(1010000, track, StringId(1), StringId(2), _))
+  EXPECT_CALL(*slice_, Begin(1010000, track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
   EXPECT_CALL(inserter,
-              AddArg(StringId(3), StringId(3), Variadic::UnsignedInteger(10u)));
+              AddArg(debug_an_1, debug_an_1, Variadic::UnsignedInteger(10u)));
 
-  EXPECT_CALL(inserter,
-              AddArg(StringId(5), StringId(5), Variadic::Boolean(true)));
+  EXPECT_CALL(inserter, AddArg(debug_an_2_child_1, debug_an_2_child_1,
+                               Variadic::Boolean(true)));
 
-  EXPECT_CALL(inserter,
-              AddArg(StringId(6), StringId(7), Variadic::String(StringId(8))));
+  EXPECT_CALL(inserter, AddArg(debug_an_2_child_2, debug_an_2_child_2_0,
+                               Variadic::String(child21)));
 
-  EXPECT_CALL(inserter, AddArg(StringId(6), StringId(9), Variadic::Real(2.2)));
+  EXPECT_CALL(inserter, AddArg(debug_an_2_child_2, debug_an_2_child_2_1,
+                               Variadic::Real(2.2)));
 
-  EXPECT_CALL(inserter,
-              AddArg(StringId(6), StringId(10), Variadic::Integer(23)));
+  EXPECT_CALL(inserter, AddArg(debug_an_2_child_2, debug_an_2_child_2_2,
+                               Variadic::Integer(23)));
 
-  EXPECT_CALL(*slice_, End(1020000, track, StringId(1), StringId(2), _))
+  EXPECT_CALL(*slice_, End(1020000, track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
 
+  EXPECT_CALL(inserter, AddArg(debug_an_3, debug_an_3, Variadic::Integer(-3)));
   EXPECT_CALL(inserter,
-              AddArg(StringId(11), StringId(11), Variadic::Integer(-3)));
+              AddArg(debug_an_4, debug_an_4, Variadic::Boolean(true)));
+  EXPECT_CALL(inserter, AddArg(debug_an_5, debug_an_5, Variadic::Real(-5.5)));
+  EXPECT_CALL(inserter, AddArg(debug_an_6, debug_an_6, Variadic::Pointer(20u)));
   EXPECT_CALL(inserter,
-              AddArg(StringId(12), StringId(12), Variadic::Boolean(true)));
+              AddArg(debug_an_7, debug_an_7, Variadic::String(val_7)));
+  EXPECT_CALL(inserter, AddArg(debug_an_8, debug_an_8, Variadic::Json(val_8)));
   EXPECT_CALL(inserter,
-              AddArg(StringId(13), StringId(13), Variadic::Real(-5.5)));
-  EXPECT_CALL(inserter,
-              AddArg(StringId(14), StringId(14), Variadic::Pointer(20u)));
-  EXPECT_CALL(inserter,
-              AddArg(StringId(15), StringId(15), Variadic::String(16)));
-  EXPECT_CALL(inserter, AddArg(StringId(17), StringId(17), Variadic::Json(18)));
-  EXPECT_CALL(inserter,
-              AddArg(StringId(19), StringId(19), Variadic::Integer(15)));
+              AddArg(debug_an_8_foo, debug_an_8_foo, Variadic::Integer(15)));
 
   context_.sorter->ExtractEventsForced();
 }
@@ -1952,28 +1875,26 @@
 
   Tokenize();
 
-  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillOnce(Return(1));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1)).WillOnce(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
   constexpr TrackId track{0u};
+
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
+  StringId file_1 = storage_->InternString("file1");
+  StringId func_1 = storage_->InternString("func1");
+
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
   MockBoundInserter inserter;
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillOnce(Return(2));
-  EXPECT_CALL(*slice_, Begin(1010000, track, StringId(1), StringId(2), _))
+  EXPECT_CALL(*slice_, Begin(1010000, track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
-  EXPECT_CALL(*storage_, InternString(base::StringView("file1")))
-      .WillOnce(Return(3));
-  EXPECT_CALL(*storage_, InternString(base::StringView("func1")))
-      .WillOnce(Return(4));
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(3)));
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(4)));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(file_1)));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(func_1)));
   EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(42)));
 
   context_.sorter->ExtractEventsForced();
@@ -2031,35 +1952,31 @@
 
   Tokenize();
 
-  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillOnce(Return(1));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1)).WillOnce(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
+
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
+  StringId body_1 = storage_->InternString("body1");
 
   constexpr TrackId track{0};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillOnce(Return(2));
-
   MockBoundInserter inserter;
-  EXPECT_CALL(*slice_, Scoped(1010000, track, StringId(1), StringId(2), 0, _))
+  EXPECT_CALL(*slice_, Scoped(1010000, track, cat_1, ev_1, 0, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(1u)));
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("body1")))
-      .WillOnce(Return(3));
-
   // Call with logMessageBody (body1 in this case).
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(StringId(3))));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(body_1)));
 
   context_.sorter->ExtractEventsForced();
 
   EXPECT_GT(context_.storage->android_log_table().row_count(), 0u);
   EXPECT_EQ(context_.storage->android_log_table().ts()[0], 1010000);
-  EXPECT_EQ(context_.storage->android_log_table().msg()[0], 3u);
+  EXPECT_EQ(context_.storage->android_log_table().msg()[0], body_1);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventParseLegacyEventIntoRawTable) {
@@ -2116,46 +2033,40 @@
 
   Tokenize();
 
-  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillOnce(Return(1));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1)).WillOnce(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
-  EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
-      .WillOnce(Return(2));
-  EXPECT_CALL(*storage_, InternString(base::StringView("scope1")))
-      .Times(1)
-      .WillRepeatedly(Return(3));
-  EXPECT_CALL(*storage_, InternString(base::StringView("?")))
-      .WillOnce(Return(4));
-  EXPECT_CALL(*storage_, InternString(base::StringView("debug.an1")))
-      .WillOnce(Return(5));
+  StringId cat_1 = storage_->InternString("cat1");
+  StringId ev_1 = storage_->InternString("ev1");
+  StringId scope_1 = storage_->InternString("scope1");
+  StringId question = storage_->InternString("?");
+  StringId debug_an_1 = storage_->InternString("debug.an1");
 
   context_.sorter->ExtractEventsForced();
 
   ::testing::Mock::VerifyAndClearExpectations(storage_);
 
-  // Verify raw_events and args contents.
-  const auto& raw_events = storage_->raw_events();
-  EXPECT_EQ(raw_events.raw_event_count(), 1u);
-  EXPECT_EQ(raw_events.timestamps()[0], 1010000);
-  EXPECT_EQ(raw_events.name_ids()[0],
+  // Verify raw_table and args contents.
+  const auto& raw_table = storage_->raw_table();
+  EXPECT_EQ(raw_table.row_count(), 1u);
+  EXPECT_EQ(raw_table.ts()[0], 1010000);
+  EXPECT_EQ(raw_table.name()[0],
             storage_->InternString("track_event.legacy_event"));
-  EXPECT_EQ(raw_events.cpus()[0], 0u);
-  EXPECT_EQ(raw_events.utids()[0], 1u);
-  EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
+  EXPECT_EQ(raw_table.cpu()[0], 0u);
+  EXPECT_EQ(raw_table.utid()[0], 1u);
+  EXPECT_EQ(raw_table.arg_set_id()[0], 1u);
 
-  EXPECT_GE(storage_->args().args_count(), 13u);
+  EXPECT_GE(storage_->arg_table().row_count(), 13u);
 
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.category"),
-                     Variadic::String(1u)));
+                     Variadic::String(cat_1)));
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.name"),
-                     Variadic::String(2u)));
+                     Variadic::String(ev_1)));
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.phase"),
-                     Variadic::String(4u)));
+                     Variadic::String(question)));
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.duration_ns"),
                      Variadic::Integer(23000)));
   EXPECT_TRUE(HasArg(1u,
@@ -2169,7 +2080,7 @@
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.global_id"),
                      Variadic::UnsignedInteger(99u)));
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.id_scope"),
-                     Variadic::String(3u)));
+                     Variadic::String(scope_1)));
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.bind_id"),
                      Variadic::UnsignedInteger(98u)));
   EXPECT_TRUE(HasArg(1u,
@@ -2177,7 +2088,7 @@
                      Variadic::Boolean(true)));
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.flow_direction"),
                      Variadic::String(storage_->InternString("inout"))));
-  EXPECT_TRUE(HasArg(1u, 5u, Variadic::UnsignedInteger(10u)));
+  EXPECT_TRUE(HasArg(1u, debug_an_1, Variadic::UnsignedInteger(10u)));
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventLegacyTimestampsWithClockSnapshot) {
@@ -2210,11 +2121,11 @@
 
   Tokenize();
 
-  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillOnce(Return(1));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
-  TraceStorage::Thread thread(16);
-  thread.upid = 1u;
-  EXPECT_CALL(*storage_, GetThread(1)).WillOnce(testing::ReturnRef(thread));
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
 
   constexpr TrackId track{0u};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
@@ -2245,8 +2156,8 @@
   context_.sorter->ExtractEventsForced();
 
   // Metadata should have created a raw event.
-  const auto& raw_events = storage_->raw_events();
-  EXPECT_EQ(raw_events.raw_event_count(), 1u);
+  const auto& raw_table = storage_->raw_table();
+  EXPECT_EQ(raw_table.row_count(), 1u);
 }
 
 TEST_F(ProtoTraceParserTest, ParseChromeMetadataEventIntoRawTable) {
@@ -2275,14 +2186,14 @@
   Tokenize();
   context_.sorter->ExtractEventsForced();
 
-  // Verify raw_events and args contents.
-  const auto& raw_events = storage_->raw_events();
-  EXPECT_EQ(raw_events.raw_event_count(), 1u);
-  EXPECT_EQ(raw_events.name_ids()[0],
+  // Verify raw_table and args contents.
+  const auto& raw_table = storage_->raw_table();
+  EXPECT_EQ(raw_table.row_count(), 1u);
+  EXPECT_EQ(raw_table.name()[0],
             storage_->InternString("chrome_event.metadata"));
-  EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
+  EXPECT_EQ(raw_table.arg_set_id()[0], 1u);
 
-  EXPECT_EQ(storage_->args().args_count(), 2u);
+  EXPECT_EQ(storage_->arg_table().row_count(), 2u);
   EXPECT_TRUE(HasArg(1u, storage_->InternString(kStringName),
                      Variadic::String(storage_->InternString(kStringValue))));
   EXPECT_TRUE(HasArg(1u, storage_->InternString(kIntName),
@@ -2309,14 +2220,14 @@
 
   context_.sorter->ExtractEventsForced();
 
-  // Verify raw_events and args contents.
-  const auto& raw_events = storage_->raw_events();
-  EXPECT_EQ(raw_events.raw_event_count(), 1u);
-  EXPECT_EQ(raw_events.name_ids()[0],
+  // Verify raw_table and args contents.
+  const auto& raw_table = storage_->raw_table();
+  EXPECT_EQ(raw_table.row_count(), 1u);
+  EXPECT_EQ(raw_table.name()[0],
             storage_->InternString("chrome_event.legacy_system_trace"));
-  EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
+  EXPECT_EQ(raw_table.arg_set_id()[0], 1u);
 
-  EXPECT_EQ(storage_->args().args_count(), 1u);
+  EXPECT_EQ(storage_->arg_table().row_count(), 1u);
   EXPECT_TRUE(HasArg(1u, storage_->InternString("data"),
                      Variadic::String(storage_->InternString(kFullData))));
 }
@@ -2340,14 +2251,14 @@
 
   context_.sorter->ExtractEventsForced();
 
-  // Verify raw_events and args contents.
-  const auto& raw_events = storage_->raw_events();
-  EXPECT_EQ(raw_events.raw_event_count(), 1u);
-  EXPECT_EQ(raw_events.name_ids()[0],
+  // Verify raw_table and args contents.
+  const auto& raw_table = storage_->raw_table();
+  EXPECT_EQ(raw_table.row_count(), 1u);
+  EXPECT_EQ(raw_table.name()[0],
             storage_->InternString("chrome_event.legacy_user_trace"));
-  EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
+  EXPECT_EQ(raw_table.arg_set_id()[0], 1u);
 
-  EXPECT_EQ(storage_->args().args_count(), 1u);
+  EXPECT_EQ(storage_->arg_table().row_count(), 1u);
   EXPECT_TRUE(
       HasArg(1u, storage_->InternString("data"),
              Variadic::String(storage_->InternString(kUserTraceEvent))));
@@ -2368,12 +2279,9 @@
 
   Tokenize();
 
-  EXPECT_CALL(*storage_, InternString(base::StringView(kName)))
-      .WillOnce(Return(1));
-  EXPECT_CALL(*storage_, InternString(base::StringView(kTag1)))
-      .WillOnce(Return(2));
-  EXPECT_CALL(*storage_, InternString(base::StringView(kTag2)))
-      .WillOnce(Return(3));
+  StringId name_1 = storage_->InternString(kName);
+  StringId tag_1 = storage_->InternString(kTag1);
+  StringId tag_2 = storage_->InternString(kTag2);
 
   StringId benchmark_id = *storage_->string_pool().GetId(
       metadata::kNames[metadata::benchmark_name]);
@@ -2391,9 +2299,9 @@
     meta_entries.emplace_back(std::make_pair(meta_keys[i], meta_values[i]));
   }
   EXPECT_THAT(meta_entries,
-              UnorderedElementsAreArray({std::make_pair(benchmark_id, 1),
-                                         std::make_pair(tags_id, 2),
-                                         std::make_pair(tags_id, 3)}));
+              UnorderedElementsAreArray({std::make_pair(benchmark_id, name_1),
+                                         std::make_pair(tags_id, tag_1),
+                                         std::make_pair(tags_id, tag_2)}));
 }
 
 TEST_F(ProtoTraceParserTest, AndroidPackagesList) {
@@ -2430,7 +2338,7 @@
   // The relevant arg sets have the info about the packages. To simplify test
   // structure, make an assumption that metadata storage is filled in in the
   // FIFO order of seen packages.
-  const auto& args = context_.storage->args();
+  const auto& args = context_.storage->arg_table();
   const auto& metadata = context_.storage->metadata_table();
 
   Table package_list = metadata.Filter(
@@ -2443,10 +2351,10 @@
 
   // helper to look up arg values
   auto find_arg = [&args, this](ArgSetId set_id, const char* arg_name) {
-    for (size_t i = 0; i < args.set_ids().size(); i++) {
-      if (args.set_ids()[i] == set_id &&
-          args.keys()[i] == storage_->InternString(arg_name))
-        return args.arg_values()[i];
+    for (uint32_t i = 0; i < args.row_count(); i++) {
+      if (args.arg_set_id()[i] == set_id &&
+          args.key()[i] == storage_->InternString(arg_name))
+        return storage_->GetArgValue(i);
     }
     PERFETTO_FATAL("Didn't find expected argument");
   };
@@ -2483,6 +2391,11 @@
 
     auto mapping = interned_data->add_mappings();
     mapping->set_iid(1);
+    mapping->set_build_id(1);
+
+    auto build_id = interned_data->add_build_ids();
+    build_id->set_iid(1);
+    build_id->set_str("3BBCFBD372448A727265C3E7C4D954F91");
 
     auto frame = interned_data->add_frames();
     frame->set_iid(1);
@@ -2525,7 +2438,6 @@
   }
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .Times(2)
       .WillRepeatedly(Return(1));
 
   Tokenize();
@@ -2534,17 +2446,24 @@
   const auto& samples = storage_->cpu_profile_stack_sample_table();
   EXPECT_EQ(samples.row_count(), 3u);
 
-  EXPECT_EQ(samples.ts()[0], 1010);
+  EXPECT_EQ(samples.ts()[0], 11000);
   EXPECT_EQ(samples.callsite_id()[0], 0);
   EXPECT_EQ(samples.utid()[0], 1u);
 
-  EXPECT_EQ(samples.ts()[1], 1025);
+  EXPECT_EQ(samples.ts()[1], 26000);
   EXPECT_EQ(samples.callsite_id()[1], 1);
   EXPECT_EQ(samples.utid()[1], 1u);
 
-  EXPECT_EQ(samples.ts()[2], 1067);
+  EXPECT_EQ(samples.ts()[2], 68000);
   EXPECT_EQ(samples.callsite_id()[2], 0);
   EXPECT_EQ(samples.utid()[2], 1u);
+
+  // Breakpad build_ids should not be modified/mangled.
+  ASSERT_STREQ(
+      context_.storage
+          ->GetString(storage_->stack_profile_mapping_table().build_id()[0])
+          .c_str(),
+      "3BBCFBD372448A727265C3E7C4D954F91");
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index b964a2c..ca746b7 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -18,8 +18,7 @@
 
 #include <string>
 
-#include <zlib.h>
-
+#include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
@@ -42,6 +41,10 @@
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#endif
+
 namespace perfetto {
 namespace trace_processor {
 
@@ -53,6 +56,7 @@
 constexpr uint8_t kTracePacketTag =
     MakeTagLengthDelimited(protos::pbzero::Trace::kPacketFieldNumber);
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 TraceBlobView Decompress(TraceBlobView input) {
   uint8_t out[4096];
   std::string s;
@@ -81,6 +85,7 @@
   memcpy(output.get(), s.data(), s.size());
   return TraceBlobView(std::move(output), 0, s.size());
 }
+#endif  //  PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 
 }  // namespace
 
@@ -309,6 +314,7 @@
   }
 
   if (decoder.has_compressed_packets()) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
     protozero::ConstBytes field = decoder.compressed_packets();
     const size_t field_off = packet.offset_of(field.data);
     TraceBlobView compressed_packets = packet.slice(field_off, field.size);
@@ -334,6 +340,9 @@
     }
 
     return util::OkStatus();
+#else
+    return util::Status("Cannot decode compressed packets. Zlib not enabled");
+#endif
   }
 
   // If we're not forcing a full sort and this is a write_into_file trace, then
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index dd143dd..5ca7ca1 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -230,8 +230,8 @@
     context_->process_tracker->UpdateThread(tid, tgid);
 
     if (thd.has_name()) {
-      StringId threadNameId = context_->storage->InternString(thd.name());
-      context_->process_tracker->UpdateThreadName(tid, threadNameId);
+      StringId thread_name_id = context_->storage->InternString(thd.name());
+      context_->process_tracker->UpdateThreadName(tid, thread_name_id);
     }
   }
 }
@@ -254,7 +254,7 @@
         continue;
       }
       bool is_counter_field = fld.id() < proc_stats_process_names_.size() &&
-                              proc_stats_process_names_[fld.id()] != 0;
+                              !proc_stats_process_names_[fld.id()].is_null();
       if (is_counter_field) {
         // Memory counters are in KB, keep values in bytes in the trace
         // processor.
diff --git a/src/trace_processor/importers/proto/track_event.descriptor.h b/src/trace_processor/importers/proto/track_event.descriptor.h
index 00c28d8..e0237e6 100644
--- a/src/trace_processor/importers/proto/track_event.descriptor.h
+++ b/src/trace_processor/importers/proto/track_event.descriptor.h
@@ -25,17 +25,17 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// f242f1ac484bbe7ba4c45e77b56ab588f8015196
+// d6628b15181dba5287e35b56b966b39ea93d42b1
 // SHA1(protos/perfetto/trace/track_event/track_event.proto)
-// 9b54f5487bfe8924c589c2c2f8cfee899f019966
+// 1cd2627d7a57ddcf4e61b967cd255011bf3a09d5
 
 // This is the proto TrackEvent encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 16147> kTrackEventDescriptor{
-    {0x0a, 0x9a, 0x08, 0x0a, 0x38, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+constexpr std::array<uint8_t, 16107> kTrackEventDescriptor{
+    {0x0a, 0x96, 0x08, 0x0a, 0x38, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61,
      0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65,
      0x6e, 0x74, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x6e, 0x6e,
@@ -122,37 +122,36 @@
      0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x69, 0x64, 0x18, 0x01,
      0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x69, 0x69, 0x64, 0x12, 0x12, 0x0a,
      0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xdb, 0x01,
-     0x0a, 0x33, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f,
-     0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f,
-     0x6c, 0x6f, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x57,
-     0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-     0x12, 0x2e, 0x0a, 0x13, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c,
-     0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x69, 0x64, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x69, 0x64,
-     0x12, 0x19, 0x0a, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x69, 0x69, 0x64,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6f, 0x64, 0x79,
-     0x49, 0x69, 0x64, 0x22, 0x36, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x4d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a,
-     0x03, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03,
-     0x69, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0x86, 0x01, 0x0a, 0x36, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
-     0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f,
-     0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65,
-     0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x37, 0x0a, 0x0d, 0x54,
-     0x61, 0x73, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e,
-     0x12, 0x26, 0x0a, 0x0f, 0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x66,
-     0x72, 0x6f, 0x6d, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x46, 0x72, 0x6f,
-     0x6d, 0x49, 0x69, 0x64, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xd6, 0x01, 0x0a,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0xd7, 0x01, 0x0a, 0x33, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63,
+     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x6c, 0x6f, 0x67, 0x5f,
+     0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x57, 0x0a, 0x0a, 0x4c, 0x6f,
+     0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x13,
+     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x04, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08,
+     0x62, 0x6f, 0x64, 0x79, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x04, 0x52, 0x07, 0x62, 0x6f, 0x64, 0x79, 0x49, 0x69, 0x64, 0x22,
+     0x36, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
+     0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x69, 0x64,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x69, 0x69, 0x64, 0x12,
+     0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x0a, 0x82, 0x01, 0x0a, 0x36,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72,
+     0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x74, 0x61,
+     0x73, 0x6b, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22,
+     0x37, 0x0a, 0x0d, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75,
+     0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x6f, 0x73, 0x74,
+     0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x69, 0x69, 0x64, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x65,
+     0x64, 0x46, 0x72, 0x6f, 0x6d, 0x49, 0x69, 0x64, 0x0a, 0xd2, 0x01, 0x0a,
      0x37, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74,
      0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x73,
@@ -170,849 +169,848 @@
      0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b,
      0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18,
      0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x69, 0x6e, 0x65, 0x4e,
-     0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xb9, 0x4d,
-     0x0a, 0x49, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f,
-     0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f,
-     0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f,
-     0x73, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75,
-     0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x37, 0x70, 0x72,
+     0x75, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0xb5, 0x4d, 0x0a, 0x49, 0x70, 0x72,
      0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63,
-     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x73, 0x6f, 0x75, 0x72,
-     0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x0b, 0x0a, 0x1e, 0x43, 0x68,
-     0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74,
-     0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74,
-     0x65, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f,
+     0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+     0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f,
+     0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+     0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x37, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72,
+     0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76,
+     0x65, 0x6e, 0x74, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c,
+     0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x22, 0xe6, 0x0b, 0x0a, 0x1e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63,
+     0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x12, 0x52, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61,
+     0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x0c,
+     0x73, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+     0x12, 0x3f, 0x0a, 0x1c, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e,
+     0x67, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x19, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e,
+     0x67, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x1e, 0x62, 0x65, 0x67,
+     0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x74,
+     0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x54, 0x61, 0x73,
+     0x6b, 0x12, 0x37, 0x0a, 0x18, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+     0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x65, 0x67, 0x69,
+     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x5b,
+     0x0a, 0x2b, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c, 0x61,
+     0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x73,
+     0x73, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64,
+     0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x26, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64,
+     0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4d, 0x69, 0x73,
+     0x73, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x44,
+     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x73,
+     0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x64,
+     0x75, 0x63, 0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18,
+     0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x70,
+     0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54,
+     0x6f, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x6e,
+     0x63, 0x79, 0x12, 0x55, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65,
+     0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
+     0x0e, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f,
+     0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+     0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74,
+     0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x41,
+     0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6f, 0x0a, 0x0d, 0x64, 0x65, 0x61,
+     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x08,
+     0x20, 0x01, 0x28, 0x0e, 0x32, 0x4a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43,
+     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
+     0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49,
+     0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61, 0x64,
+     0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0c, 0x64, 0x65,
+     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1f,
+     0x0a, 0x0b, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75,
+     0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x61,
+     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x64,
+     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65,
+     0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x73, 0x18,
+     0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x64, 0x65, 0x61, 0x64, 0x6c,
+     0x69, 0x6e, 0x65, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64,
+     0x41, 0x74, 0x55, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x6f, 0x77, 0x5f,
+     0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6e, 0x6f,
+     0x77, 0x55, 0x73, 0x12, 0x36, 0x0a, 0x18, 0x6e, 0x6f, 0x77, 0x5f, 0x74,
+     0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64,
+     0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x14, 0x6e, 0x6f, 0x77, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64,
+     0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
+     0x4e, 0x0a, 0x25, 0x6e, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65,
+     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64,
+     0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74,
+     0x61, 0x5f, 0x75, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f,
+     0x6e, 0x6f, 0x77, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
+     0x65, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74,
+     0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x56, 0x0a, 0x15, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x0e, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67,
+     0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41,
+     0x72, 0x67, 0x73, 0x52, 0x12, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d,
+     0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12,
+     0x65, 0x0a, 0x1a, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x5f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
+     0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x28, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x17, 0x62, 0x65, 0x67, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65,
+     0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x5f, 0x0a, 0x18, 0x62, 0x65,
+     0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f,
+     0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x10,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75,
+     0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x15, 0x62, 0x65,
+     0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72,
+     0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x64, 0x0a, 0x19, 0x63,
+     0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x69,
+     0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
+     0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x54,
+     0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
+     0x52, 0x17, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+     0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72,
+     0x79, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49,
+     0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61, 0x64,
+     0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x19,
+     0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44,
+     0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+     0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x41, 0x44, 0x4c,
+     0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e,
+     0x45, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x41, 0x44, 0x4c,
+     0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4d, 0x4d,
+     0x45, 0x44, 0x49, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15,
+     0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44,
+     0x45, 0x5f, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52, 0x10, 0x03, 0x12,
+     0x16, 0x0a, 0x12, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f,
+     0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x04, 0x12,
+     0x19, 0x0a, 0x15, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f,
+     0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44,
+     0x10, 0x05, 0x22, 0x86, 0x28, 0x0a, 0x1c, 0x43, 0x68, 0x72, 0x6f, 0x6d,
+     0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12,
+     0x59, 0x0a, 0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
+     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f,
+     0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6d, 0x61, 0x6a, 0x6f,
+     0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x59, 0x0a, 0x0b, 0x6d, 0x69,
+     0x6e, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
      0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68,
      0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74,
      0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
-     0x6e, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63,
-     0x68, 0x69, 0x6e, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x6f, 0x62, 0x73, 0x65,
-     0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x6f, 0x62, 0x73, 0x65,
-     0x72, 0x76, 0x69, 0x6e, 0x67, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x42, 0x0a,
-     0x1e, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
-     0x6e, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x1a, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
-     0x65, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x37, 0x0a, 0x18, 0x70, 0x65, 0x6e,
-     0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20,
-     0x01, 0x28, 0x08, 0x52, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
-     0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x61,
-     0x73, 0x6b, 0x12, 0x5b, 0x0a, 0x2b, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65,
-     0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x63, 0x65,
-     0x65, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e,
-     0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x26, 0x73, 0x6b, 0x69,
-     0x70, 0x70, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x4d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x65,
-     0x64, 0x65, 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12,
-     0x4d, 0x0a, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c,
-     0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f,
-     0x5f, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65,
-     0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x73,
-     0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x4c,
-     0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x55, 0x0a, 0x0d, 0x69, 0x6e,
-     0x73, 0x69, 0x64, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
-     0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
-     0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
-     0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x69, 0x6e, 0x73,
-     0x69, 0x64, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6f, 0x0a,
-     0x0d, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6d, 0x6f,
-     0x64, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4a, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
-     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64,
-     0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65,
-     0x52, 0x0c, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f,
-     0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
-     0x6e, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x0a, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x73, 0x12,
-     0x37, 0x0a, 0x18, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f,
-     0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74,
-     0x5f, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x64,
-     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x63, 0x68, 0x65, 0x64,
-     0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x15, 0x0a, 0x06,
-     0x6e, 0x6f, 0x77, 0x5f, 0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x05, 0x6e, 0x6f, 0x77, 0x55, 0x73, 0x12, 0x36, 0x0a, 0x18, 0x6e,
-     0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
-     0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18,
-     0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6e, 0x6f, 0x77, 0x54, 0x6f,
-     0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x74,
-     0x61, 0x55, 0x73, 0x12, 0x4e, 0x0a, 0x25, 0x6e, 0x6f, 0x77, 0x5f, 0x74,
-     0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73,
-     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f,
-     0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x0d, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x1f, 0x6e, 0x6f, 0x77, 0x54, 0x6f, 0x44, 0x65, 0x61,
-     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
-     0x65, 0x64, 0x41, 0x74, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
-     0x56, 0x0a, 0x15, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70,
-     0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73,
-     0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x12, 0x62, 0x65, 0x67,
-     0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41,
-     0x72, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x1a, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6f, 0x62, 0x73, 0x65, 0x72,
-     0x76, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0f, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65,
-     0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x17, 0x62,
-     0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73,
-     0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x5f,
-     0x0a, 0x18, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61,
-     0x74, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x52, 0x15, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
-     0x64, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
-     0x72, 0x5f, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x69, 0x73,
-     0x74, 0x6f, 0x72, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
-     0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73,
-     0x74, 0x6f, 0x72, 0x79, 0x52, 0x17, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
-     0x69, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69,
-     0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x4d, 0x6f, 0x64, 0x65,
-     0x12, 0x1d, 0x0a, 0x19, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45,
-     0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
-     0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x44,
-     0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
-     0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44,
-     0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
-     0x5f, 0x49, 0x4d, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x54, 0x45, 0x10, 0x02,
-     0x12, 0x19, 0x0a, 0x15, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45,
-     0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41,
-     0x52, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x41, 0x44, 0x4c,
-     0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4c, 0x41, 0x54,
-     0x45, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x45, 0x41, 0x44, 0x4c,
-     0x49, 0x4e, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x42, 0x4c, 0x4f,
-     0x43, 0x4b, 0x45, 0x44, 0x10, 0x05, 0x22, 0x86, 0x28, 0x0a, 0x1c, 0x43,
-     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
-     0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
-     0x69, 0x6e, 0x65, 0x12, 0x59, 0x0a, 0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72,
-     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-     0x32, 0x38, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x6e, 0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74,
+     0x65, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74,
+     0x65, 0x1a, 0xf9, 0x0a, 0x0a, 0x0a, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74,
+     0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x0e, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f,
+     0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+     0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74,
+     0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x41, 0x63, 0x74,
+     0x69, 0x6f, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x16, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
+     0x32, 0x4c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
      0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
      0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
      0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e,
-     0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a,
-     0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x59,
-     0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x65,
+     0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x13, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x16, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e,
+     0x32, 0x4c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
+     0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e,
+     0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x13, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x12, 0x8e, 0x01, 0x0a, 0x1b, 0x6c, 0x61, 0x79, 0x65,
+     0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
+     0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x50, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
+     0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63,
+     0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65,
+     0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x52, 0x17, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72,
+     0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x13, 0x66, 0x6f, 0x72,
+     0x63, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x73,
+     0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x53,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43,
+     0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61,
+     0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x46, 0x6f, 0x72,
+     0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77, 0x4f, 0x6e, 0x54,
+     0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
+     0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61,
+     0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xa1, 0x01, 0x0a, 0x13, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45,
+     0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41,
+     0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+     0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49,
+     0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x42,
+     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52,
+     0x41, 0x4d, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x42,
+     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02,
+     0x12, 0x24, 0x0a, 0x20, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d,
+     0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x4e, 0x53,
+     0x49, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45,
+     0x10, 0x03, 0x22, 0x93, 0x01, 0x0a, 0x13, 0x42, 0x65, 0x67, 0x69, 0x6e,
+     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f,
+     0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55,
+     0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
+     0x12, 0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41,
+     0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x44, 0x4c,
+     0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49, 0x4e,
+     0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f,
+     0x53, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x42, 0x45,
+     0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
+     0x4d, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x54, 0x4f, 0x5f,
+     0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x22, 0xf4, 0x01, 0x0a,
+     0x17, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x12, 0x20, 0x0a, 0x1c, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52,
+     0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53,
+     0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19,
+     0x0a, 0x15, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10,
+     0x01, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54,
+     0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x43,
+     0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x4c, 0x41,
+     0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41,
+     0x4d, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x49, 0x4e, 0x47, 0x10,
+     0x03, 0x12, 0x2d, 0x0a, 0x29, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54,
+     0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x57, 0x41,
+     0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x46, 0x49,
+     0x52, 0x53, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04,
+     0x12, 0x31, 0x0a, 0x2d, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52,
+     0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x57, 0x41, 0x49,
+     0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x46, 0x49, 0x52,
+     0x53, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f,
+     0x4e, 0x10, 0x05, 0x22, 0xc7, 0x01, 0x0a, 0x1a, 0x46, 0x6f, 0x72, 0x63,
+     0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77, 0x4f, 0x6e, 0x54, 0x69,
+     0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d,
+     0x0a, 0x19, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44,
+     0x52, 0x41, 0x57, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
+     0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, 0x52,
+     0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x49,
+     0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x46, 0x4f, 0x52,
+     0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x57,
+     0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x43,
+     0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x02, 0x12, 0x28, 0x0a, 0x24, 0x46,
+     0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57,
+     0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52,
+     0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10,
+     0x03, 0x12, 0x22, 0x0a, 0x1e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f,
+     0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49,
+     0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x10,
+     0x04, 0x1a, 0xb3, 0x1b, 0x0a, 0x0a, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d,
+     0x69, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65,
+     0x6e, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d,
+     0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x63,
+     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e,
+     0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x22, 0x6c, 0x61, 0x73,
+     0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
+     0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x70, 0x65,
+     0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x1e, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74,
+     0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x46, 0x0a,
+     0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
+     0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f,
+     0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x05, 0x52, 0x1c, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x72, 0x61, 0x77,
+     0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x52, 0x0a,
+     0x27, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
+     0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
+     0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
+     0x73, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x21,
+     0x6c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d,
+     0x62, 0x65, 0x72, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a,
+     0x08, 0x64, 0x69, 0x64, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x06, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x64, 0x44, 0x72, 0x61, 0x77,
+     0x12, 0x59, 0x0a, 0x2b, 0x64, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x6e, 0x64,
+     0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x63, 0x75,
+     0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18,
+     0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x64, 0x69, 0x64, 0x53, 0x65,
+     0x6e, 0x64, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65,
+     0x6e, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x5f, 0x0a, 0x2e, 0x64,
+     0x69, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x65,
+     0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63,
+     0x74, 0x65, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x08, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x27, 0x64, 0x69, 0x64, 0x4e, 0x6f, 0x74, 0x69,
+     0x66, 0x79, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65, 0x63,
+     0x74, 0x65, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x5d, 0x0a, 0x2d,
+     0x64, 0x69, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65,
+     0x63, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x6f, 0x6e, 0x18, 0x09, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x26, 0x64, 0x69, 0x64, 0x4e, 0x6f, 0x74, 0x69,
+     0x66, 0x79, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65, 0x63,
+     0x74, 0x65, 0x64, 0x53, 0x6f, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x23, 0x77,
+     0x61, 0x6e, 0x74, 0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d,
+     0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f,
+     0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x0a,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64,
+     0x12, 0x35, 0x0a, 0x17, 0x64, 0x69, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d,
+     0x69, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64,
+     0x69, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x69,
+     0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x64,
+     0x69, 0x64, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+     0x65, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18,
+     0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x64, 0x69, 0x64, 0x49, 0x6e,
+     0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x79, 0x65,
+     0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69,
+     0x6e, 0x6b, 0x12, 0x48, 0x0a, 0x21, 0x64, 0x69, 0x64, 0x5f, 0x70, 0x65,
+     0x72, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73,
+     0x69, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
+     0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x64,
+     0x69, 0x64, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x6d, 0x70,
+     0x6c, 0x53, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
+     0x61, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x69, 0x64, 0x5f,
+     0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65,
+     0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x64,
+     0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x73,
+     0x12, 0x4e, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74,
+     0x69, 0x76, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x62,
+     0x6f, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69,
+     0x6f, 0x6e, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x21, 0x63,
+     0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x43, 0x68,
+     0x65, 0x63, 0x6b, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x41, 0x6e,
+     0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x15,
+     0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x75, 0x62, 0x6d,
+     0x69, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x10, 0x20,
+     0x01, 0x28, 0x05, 0x52, 0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+     0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73,
+     0x12, 0x63, 0x0a, 0x30, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63,
+     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72,
+     0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
+     0x73, 0x69, 0x6e, 0x6b, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x29,
+     0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73,
+     0x57, 0x69, 0x74, 0x68, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4c,
+     0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x65,
+     0x64, 0x73, 0x5f, 0x72, 0x65, 0x64, 0x72, 0x61, 0x77, 0x18, 0x12, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x52, 0x65,
+     0x64, 0x72, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x65, 0x65, 0x64,
+     0x73, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69,
+     0x6c, 0x65, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6e,
+     0x65, 0x65, 0x64, 0x73, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54,
+     0x69, 0x6c, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x6e, 0x65, 0x65, 0x64,
+     0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x13, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x42, 0x65, 0x67, 0x69, 0x6e,
+     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a,
+     0x1a, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x6f, 0x6e, 0x65, 0x5f, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6e,
+     0x65, 0x65, 0x64, 0x73, 0x4f, 0x6e, 0x65, 0x42, 0x65, 0x67, 0x69, 0x6e,
+     0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a,
+     0x07, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x18, 0x16, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x07, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x12,
+     0x39, 0x0a, 0x19, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x61,
+     0x75, 0x73, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16,
+     0x62, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f,
+     0x75, 0x72, 0x63, 0x65, 0x50, 0x61, 0x75, 0x73, 0x65, 0x64, 0x12, 0x19,
+     0x0a, 0x08, 0x63, 0x61, 0x6e, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x18,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x61, 0x6e, 0x44, 0x72, 0x61,
+     0x77, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+     0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x19,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+     0x63, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x44, 0x72, 0x61, 0x77, 0x12, 0x28,
+     0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e,
+     0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x0e, 0x68, 0x61, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+     0x54, 0x72, 0x65, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x70, 0x65, 0x6e, 0x64,
+     0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x73, 0x5f,
+     0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x63,
+     0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1b, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x1f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54,
+     0x72, 0x65, 0x65, 0x49, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x46, 0x6f,
+     0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+     0x3e, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72,
+     0x65, 0x65, 0x5f, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x66, 0x69, 0x72,
+     0x73, 0x74, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18, 0x1c, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65,
+     0x65, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x46, 0x69, 0x72, 0x73, 0x74, 0x44,
+     0x72, 0x61, 0x77, 0x12, 0x3d, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76,
+     0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x72, 0x65,
+     0x61, 0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18,
+     0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76,
+     0x65, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79,
+     0x54, 0x6f, 0x44, 0x72, 0x61, 0x77, 0x12, 0x6c, 0x0a, 0x35, 0x64, 0x69,
+     0x64, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64,
+     0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x5f,
+     0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f,
+     0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73,
+     0x69, 0x6e, 0x6b, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2d, 0x64,
+     0x69, 0x64, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x49,
+     0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x46, 0x69, 0x72,
+     0x73, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x6a, 0x0a, 0x0d,
+     0x74, 0x72, 0x65, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74,
+     0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x45, 0x2e, 0x70, 0x65,
      0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
      0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70,
      0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d,
      0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72,
-     0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x6f, 0x72,
-     0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0xf9, 0x0a, 0x0a, 0x0a, 0x4d, 0x61,
-     0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x51, 0x0a, 0x0b,
-     0x6e, 0x65, 0x78, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
-     0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
-     0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6e, 0x65, 0x78,
-     0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x16,
-     0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x0e, 0x32, 0x4c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43,
-     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
-     0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
-     0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x13,
-     0x62, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x16,
-     0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x0e, 0x32, 0x4c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43,
-     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
-     0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
-     0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x13,
-     0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x8e, 0x01, 0x0a, 0x1b,
-     0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x74,
-     0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x50, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f,
-     0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x61, 0x6a,
-     0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x61, 0x79, 0x65,
-     0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69,
-     0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x17, 0x6c, 0x61, 0x79,
-     0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53,
-     0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x83, 0x01, 0x0a,
-     0x13, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x72,
-     0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x0e, 0x32, 0x53, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x50, 0x72,
+     0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x65,
+     0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x7d, 0x0a, 0x14,
+     0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c,
+     0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x20, 0x20, 0x01,
+     0x28, 0x0e, 0x32, 0x4b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72,
      0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
      0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-     0x65, 0x2e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61,
-     0x77, 0x4f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52,
-     0x65, 0x64, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xa1,
-     0x01, 0x0a, 0x13, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20,
-     0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c,
-     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
-     0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15,
-     0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12,
-     0x27, 0x0a, 0x23, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4d, 0x50,
-     0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49,
-     0x44, 0x45, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
-     0x4d, 0x45, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x42, 0x45, 0x47, 0x49,
-     0x4e, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
-     0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x41, 0x44,
-     0x4c, 0x49, 0x4e, 0x45, 0x10, 0x03, 0x22, 0x93, 0x01, 0x0a, 0x13, 0x42,
+     0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x2e, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c,
+     0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x12, 0x73, 0x63, 0x72,
+     0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x2d, 0x63, 0x72, 0x69, 0x74, 0x69,
+     0x63, 0x61, 0x6c, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61,
+     0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f,
+     0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x73, 0x5f,
+     0x66, 0x61, 0x73, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x52, 0x26,
+     0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x42, 0x65, 0x67, 0x69,
+     0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x6f,
+     0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x73, 0x46, 0x61,
+     0x73, 0x74, 0x12, 0x46, 0x0a, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x74,
+     0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64,
+     0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69,
+     0x6e, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x6d, 0x61,
+     0x69, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x4d, 0x69, 0x73, 0x73,
+     0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69,
+     0x6e, 0x65, 0x12, 0x5b, 0x0a, 0x2c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e,
+     0x65, 0x78, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61,
+     0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f,
+     0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e,
+     0x63, 0x79, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x73, 0x6b,
+     0x69, 0x70, 0x4e, 0x65, 0x78, 0x74, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
+     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x52, 0x65,
+     0x64, 0x75, 0x63, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12,
+     0x37, 0x0a, 0x18, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x5f, 0x6e, 0x65, 0x65,
+     0x64, 0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x73, 0x18, 0x24, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x76,
+     0x69, 0x64, 0x65, 0x6f, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x42, 0x65, 0x67,
+     0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16,
+     0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
+     0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x25,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x65, 0x66, 0x65, 0x72, 0x42,
      0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45,
-     0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
-     0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
-     0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x45, 0x47, 0x49,
-     0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
-     0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x42,
-     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52,
-     0x41, 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x24,
-     0x0a, 0x20, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e,
-     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59,
-     0x5f, 0x54, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03,
-     0x22, 0xf4, 0x01, 0x0a, 0x17, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72,
-     0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x4c, 0x41, 0x59, 0x45,
-     0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
-     0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
-     0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f,
-     0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e,
-     0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x41, 0x59,
-     0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d,
-     0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x1d,
-     0x0a, 0x19, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45,
-     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54,
-     0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x4c, 0x41, 0x59,
-     0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d,
-     0x45, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f,
-     0x52, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x4d,
-     0x49, 0x54, 0x10, 0x04, 0x12, 0x31, 0x0a, 0x2d, 0x4c, 0x41, 0x59, 0x45,
-     0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
-     0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52,
-     0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
-     0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x22, 0xc7, 0x01, 0x0a, 0x1a,
-     0x46, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77,
-     0x4f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44,
-     0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x55, 0x4e, 0x53, 0x50,
-     0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a,
-     0x12, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52,
-     0x41, 0x57, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a,
-     0x20, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52,
-     0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46,
-     0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x02, 0x12,
-     0x28, 0x0a, 0x24, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x52, 0x45,
-     0x44, 0x52, 0x41, 0x57, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47,
-     0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54,
-     0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e, 0x46, 0x4f, 0x52,
-     0x43, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x57,
-     0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x44,
-     0x52, 0x41, 0x57, 0x10, 0x04, 0x1a, 0xb3, 0x1b, 0x0a, 0x0a, 0x4d, 0x69,
-     0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c,
-     0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d,
-     0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63,
-     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x05, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x4a, 0x0a,
-     0x22, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
-     0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69,
-     0x74, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x6c, 0x61, 0x73, 0x74, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75,
-     0x62, 0x6d, 0x69, 0x74, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
-     0x64, 0x12, 0x46, 0x0a, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x64,
-     0x72, 0x61, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
-     0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1c, 0x6c, 0x61, 0x73,
-     0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
-     0x44, 0x72, 0x61, 0x77, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65,
-     0x64, 0x12, 0x52, 0x0a, 0x27, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x62,
-     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x05, 0x52, 0x21, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x6e,
-     0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x69, 0x64, 0x5f, 0x64, 0x72, 0x61,
-     0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x64,
-     0x44, 0x72, 0x61, 0x77, 0x12, 0x59, 0x0a, 0x2b, 0x64, 0x69, 0x64, 0x5f,
-     0x73, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d,
-     0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f,
-     0x72, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x64,
-     0x69, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
-     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x46, 0x6f, 0x72, 0x43,
-     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12,
-     0x5f, 0x0a, 0x2e, 0x64, 0x69, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66,
-     0x79, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65,
-     0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69,
-     0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, 0x64, 0x69, 0x64,
-     0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
-     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45,
-     0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c,
-     0x12, 0x5d, 0x0a, 0x2d, 0x64, 0x69, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x69,
-     0x66, 0x79, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69,
-     0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f,
-     0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x6f,
-     0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x26, 0x64, 0x69, 0x64,
-     0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d,
-     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45,
-     0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x53, 0x6f, 0x6f, 0x6e, 0x12,
-     0x4b, 0x0a, 0x23, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x5f, 0x62, 0x65, 0x67,
-     0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
-     0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x77, 0x61,
-     0x6e, 0x74, 0x73, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x70, 0x65,
-     0x63, 0x74, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x64, 0x69, 0x64, 0x5f,
-     0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x69, 0x6e,
-     0x67, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x14, 0x64, 0x69, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
-     0x44, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12,
-     0x4d, 0x0a, 0x24, 0x64, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
-     0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f,
-     0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73,
-     0x69, 0x6e, 0x6b, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x64,
-     0x69, 0x64, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65,
-     0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x48, 0x0a, 0x21, 0x64, 0x69,
-     0x64, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x69, 0x6d,
-     0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x61,
-     0x6c, 0x69, 0x64, 0x61, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x1d, 0x64, 0x69, 0x64, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72,
-     0x6d, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x76,
-     0x61, 0x6c, 0x69, 0x64, 0x61, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11,
-     0x64, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f,
-     0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x0f, 0x64, 0x69, 0x64, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54,
-     0x69, 0x6c, 0x65, 0x73, 0x12, 0x4e, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x73,
-     0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63,
-     0x6b, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x6e, 0x69,
-     0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28,
-     0x05, 0x52, 0x21, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69,
-     0x76, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x62, 0x6f, 0x61,
-     0x72, 0x64, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-     0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f,
-     0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x70, 0x65, 0x6e,
-     0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x73, 0x12, 0x63, 0x0a, 0x30, 0x73, 0x75, 0x62, 0x6d,
-     0x69, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x77, 0x69,
-     0x74, 0x68, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6c,
-     0x61, 0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x11, 0x20, 0x01,
-     0x28, 0x05, 0x52, 0x29, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x73, 0x57, 0x69, 0x74, 0x68, 0x43, 0x75, 0x72, 0x72,
-     0x65, 0x6e, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x72, 0x65, 0x65,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a,
-     0x0c, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x72, 0x65, 0x64, 0x72, 0x61,
-     0x77, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x65, 0x65,
-     0x64, 0x73, 0x52, 0x65, 0x64, 0x72, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13,
-     0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72,
-     0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x11, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x50, 0x72, 0x65, 0x70,
-     0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16,
-     0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
-     0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x14,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x42,
-     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x6f,
-     0x6e, 0x65, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x70,
-     0x6c, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x16, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x4f, 0x6e, 0x65, 0x42,
-     0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65,
-     0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x69, 0x73, 0x69,
-     0x62, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x5f, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x16, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x61, 0x75, 0x73,
-     0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x5f, 0x64, 0x72,
-     0x61, 0x77, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x61,
-     0x6e, 0x44, 0x72, 0x61, 0x77, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x73,
-     0x6f, 0x75, 0x72, 0x63, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x72,
-     0x61, 0x77, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x44, 0x72,
-     0x61, 0x77, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x70, 0x65,
-     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x1a,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x50, 0x65, 0x6e,
-     0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x12, 0x4d, 0x0a, 0x24,
-     0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65,
-     0x5f, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x66, 0x6f,
-     0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x70, 0x65, 0x6e, 0x64,
-     0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x52, 0x65, 0x61,
-     0x64, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76,
-     0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x65, 0x65, 0x64, 0x73,
-     0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x64, 0x72, 0x61, 0x77, 0x18,
-     0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76,
-     0x65, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x46, 0x69,
-     0x72, 0x73, 0x74, 0x44, 0x72, 0x61, 0x77, 0x12, 0x3d, 0x0a, 0x1c, 0x61,
-     0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69,
-     0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x64,
-     0x72, 0x61, 0x77, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x61,
-     0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x52,
-     0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x44, 0x72, 0x61, 0x77, 0x12, 0x6c,
-     0x0a, 0x35, 0x64, 0x69, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
-     0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
-     0x69, 0x7a, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x61,
-     0x79, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x1e, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x2d, 0x64, 0x69, 0x64, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
-     0x41, 0x6e, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a,
-     0x65, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x54,
-     0x72, 0x65, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x6b,
-     0x12, 0x6a, 0x0a, 0x0d, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x70, 0x72, 0x69,
-     0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0e, 0x32,
-     0x45, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
-     0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d,
-     0x69, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x54, 0x72,
-     0x65, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x0c,
-     0x74, 0x72, 0x65, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79,
-     0x12, 0x7d, 0x0a, 0x14, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x68,
-     0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
-     0x18, 0x20, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x4b, 0x2e, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
-     0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x61,
-     0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48,
-     0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
-     0x12, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c,
-     0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x2d, 0x63,
-     0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x65, 0x67, 0x69,
-     0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
-     0x5f, 0x69, 0x73, 0x5f, 0x66, 0x61, 0x73, 0x74, 0x18, 0x21, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x26, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-     0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x54, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
-     0x49, 0x73, 0x46, 0x61, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x20, 0x6d, 0x61,
-     0x69, 0x6e, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x69,
-     0x73, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65,
-     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x1c, 0x6d, 0x61, 0x69, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64,
-     0x4d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x4c, 0x61, 0x73, 0x74, 0x44, 0x65,
-     0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x5b, 0x0a, 0x2c, 0x73, 0x6b,
-     0x69, 0x70, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69,
-     0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x5f, 0x6c,
-     0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x25, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65, 0x78, 0x74, 0x42, 0x65,
-     0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x54, 0x6f, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x4c, 0x61, 0x74, 0x65,
-     0x6e, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x18, 0x76, 0x69, 0x64, 0x65, 0x6f,
-     0x5f, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x24, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x15, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x4e, 0x65, 0x65, 0x64,
-     0x73, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73,
-     0x12, 0x33, 0x0a, 0x16, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x62, 0x65,
-     0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x65,
-     0x66, 0x65, 0x72, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x6c, 0x61, 0x73,
-     0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x68, 0x61, 0x64,
-     0x5f, 0x6e, 0x6f, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18,
-     0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x43,
-     0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x48, 0x61, 0x64, 0x4e, 0x6f, 0x55, 0x70,
-     0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x16, 0x64, 0x69, 0x64,
-     0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73,
-     0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x27, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x12, 0x64, 0x69, 0x64, 0x44, 0x72, 0x61, 0x77, 0x49, 0x6e,
-     0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a,
-     0x18, 0x64, 0x69, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f,
-     0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x64,
-     0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x6e, 0x65, 0x65,
-     0x64, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65,
-     0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x6e, 0x65, 0x65,
-     0x64, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x49, 0x6e,
-     0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47,
-     0x0a, 0x21, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x65,
-     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69,
-     0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18,
-     0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65,
-     0x6e, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65,
-     0x65, 0x49, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x12,
-     0x4b, 0x0a, 0x23, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f,
-     0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65,
-     0x5f, 0x77, 0x61, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69,
-     0x64, 0x65, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x70, 0x72,
-     0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e,
-     0x67, 0x54, 0x72, 0x65, 0x65, 0x57, 0x61, 0x73, 0x49, 0x6d, 0x70, 0x6c,
-     0x53, 0x69, 0x64, 0x65, 0x12, 0x5f, 0x0a, 0x2d, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x69, 0x6d, 0x61,
-     0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74,
-     0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65,
-     0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x28, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x41,
-     0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b,
-     0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76,
-     0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x61, 0x0a, 0x2e, 0x70, 0x72, 0x6f,
-     0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x69, 0x6d,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65,
-     0x74, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69,
-     0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x29, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e,
-     0x67, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f,
-     0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x50, 0x65, 0x6e,
-     0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x12, 0x59, 0x0a, 0x2a,
-     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70,
-     0x61, 0x69, 0x6e, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74,
-     0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e,
-     0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x25, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67,
-     0x50, 0x61, 0x69, 0x6e, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74,
-     0x73, 0x46, 0x6f, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54,
-     0x72, 0x65, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x65, 0x65,
-     0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x19,
+     0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x6f,
+     0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x68, 0x61, 0x64, 0x5f, 0x6e, 0x6f, 0x5f,
+     0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x26, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69,
+     0x74, 0x48, 0x61, 0x64, 0x4e, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+     0x73, 0x12, 0x32, 0x0a, 0x16, 0x64, 0x69, 0x64, 0x5f, 0x64, 0x72, 0x61,
+     0x77, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x18, 0x27, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64,
+     0x69, 0x64, 0x44, 0x72, 0x61, 0x77, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x18, 0x64, 0x69, 0x64,
+     0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6c,
+     0x61, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x64, 0x53, 0x75, 0x62, 0x6d,
+     0x69, 0x74, 0x49, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x69,
+     0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x76,
+     0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x29, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x19, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x49, 0x6d,
+     0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69,
+     0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x21, 0x63, 0x75,
+     0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e,
+     0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x69, 0x6d,
+     0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x65,
+     0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x49, 0x73, 0x49,
+     0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65, 0x12, 0x4b, 0x0a, 0x23, 0x70,
+     0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x70, 0x65, 0x6e, 0x64,
+     0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x77, 0x61, 0x73,
+     0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, 0x2b,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f,
+     0x75, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65,
+     0x65, 0x57, 0x61, 0x73, 0x49, 0x6d, 0x70, 0x6c, 0x53, 0x69, 0x64, 0x65,
+     0x12, 0x5f, 0x0a, 0x2d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69,
+     0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+     0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x5f, 0x66, 0x6f,
+     0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x65,
+     0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x28, 0x70, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x69, 0x6d, 0x61,
+     0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73,
+     0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x65,
+     0x65, 0x12, 0x61, 0x0a, 0x2e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x5f, 0x66,
+     0x6f, 0x72, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74,
+     0x72, 0x65, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x70,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x69,
+     0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65,
+     0x74, 0x73, 0x46, 0x6f, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
+     0x54, 0x72, 0x65, 0x65, 0x12, 0x59, 0x0a, 0x2a, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74,
+     0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x5f, 0x66, 0x6f,
+     0x72, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72,
+     0x65, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x6e,
+     0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x65, 0x74, 0x73, 0x46, 0x6f, 0x72,
+     0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x22,
+     0xb8, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x65, 0x65, 0x50, 0x72, 0x69, 0x6f,
+     0x72, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x45, 0x45,
+     0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e,
+     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
+     0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f,
+     0x52, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x41, 0x4d, 0x45, 0x5f, 0x50, 0x52,
+     0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x42,
+     0x4f, 0x54, 0x48, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x53, 0x10, 0x01, 0x12,
+     0x2b, 0x0a, 0x27, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f,
+     0x52, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x4d, 0x4f, 0x4f, 0x54, 0x48, 0x4e,
+     0x45, 0x53, 0x53, 0x5f, 0x54, 0x41, 0x4b, 0x45, 0x53, 0x5f, 0x50, 0x52,
+     0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x10, 0x02, 0x12, 0x2c, 0x0a, 0x28,
      0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54,
-     0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
-     0x44, 0x10, 0x00, 0x12, 0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x45, 0x45, 0x5f,
-     0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x41, 0x4d,
-     0x45, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x46,
-     0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x5f, 0x54, 0x52, 0x45, 0x45,
-     0x53, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x54, 0x52, 0x45, 0x45, 0x5f,
-     0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x4d, 0x4f,
-     0x4f, 0x54, 0x48, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x41, 0x4b, 0x45,
-     0x53, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x10, 0x02,
-     0x12, 0x2c, 0x0a, 0x28, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x52, 0x49,
-     0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x45, 0x57, 0x5f, 0x43, 0x4f,
-     0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x41, 0x4b, 0x45, 0x53, 0x5f,
-     0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x10, 0x03, 0x22, 0x82,
-     0x01, 0x0a, 0x12, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e,
-     0x64, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a,
-     0x1a, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44,
-     0x4c, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
-     0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x52,
-     0x4f, 0x4c, 0x4c, 0x5f, 0x41, 0x46, 0x46, 0x45, 0x43, 0x54, 0x53, 0x5f,
-     0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c,
-     0x45, 0x52, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, 0x53, 0x43, 0x52, 0x4f,
-     0x4c, 0x4c, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f,
-     0x41, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c,
-     0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x52, 0x10, 0x02, 0x22,
-     0x8f, 0x05, 0x0a, 0x0e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x46, 0x0a, 0x04, 0x74, 0x79,
-     0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x54, 0x79, 0x70, 0x65,
-     0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12,
-     0x27, 0x0a, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f,
-     0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
-     0x52, 0x0e, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75,
-     0x6d, 0x62, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x04, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69,
-     0x6d, 0x65, 0x55, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x61, 0x64,
-     0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x0a, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x55,
-     0x73, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
-     0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x06,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
-     0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x28, 0x0a,
-     0x10, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-     0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x0e, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50,
-     0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6e, 0x69, 0x6d, 0x61,
-     0x74, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x0b, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x4f, 0x6e,
-     0x6c, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-     0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x69,
-     0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x11, 0x73,
-     0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x49, 0x69, 0x64, 0x12, 0x4a, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72,
-     0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
-     0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69,
-     0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-     0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x01, 0x0a,
-     0x12, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41,
-     0x72, 0x67, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x42,
-     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41,
-     0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53,
-     0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21,
-     0x0a, 0x1d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d,
-     0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
-     0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a,
-     0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
-     0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e,
-     0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x42,
-     0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41,
-     0x52, 0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x49, 0x53,
-     0x53, 0x45, 0x44, 0x10, 0x03, 0x42, 0x0e, 0x0a, 0x0c, 0x63, 0x72, 0x65,
-     0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0xf5, 0x05,
-     0x0a, 0x12, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d,
-     0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75,
-     0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x75, 0x70, 0x64,
-     0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x24, 0x0a, 0x0e,
-     0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f,
-     0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x66, 0x69,
-     0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x3f,
-     0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x0e, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69,
-     0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
-     0x67, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74,
-     0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65,
-     0x6e, 0x74, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x48, 0x00,
-     0x52, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x67,
-     0x73, 0x12, 0x3e, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x72,
-     0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x41, 0x72, 0x67, 0x73, 0x48, 0x00, 0x52, 0x08, 0x6c, 0x61, 0x73,
-     0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x10, 0x74, 0x69, 0x6d,
-     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75,
-     0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x54, 0x69, 0x6d,
-     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x52,
-     0x0e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49,
-     0x6e, 0x55, 0x73, 0x1a, 0xad, 0x02, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65,
-     0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x12, 0x25,
-     0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x64,
-     0x65, 0x6c, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d,
-     0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74,
-     0x61, 0x12, 0x31, 0x0a, 0x15, 0x6e, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f,
-     0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c,
-     0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6e, 0x6f,
-     0x77, 0x54, 0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44,
-     0x65, 0x6c, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x17, 0x66, 0x72, 0x61, 0x6d,
-     0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f,
-     0x77, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x13, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65,
-     0x54, 0x6f, 0x4e, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x3e,
-     0x0a, 0x1c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
-     0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
-     0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x18, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x54,
-     0x6f, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c,
-     0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x05, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x03, 0x6e, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54,
-     0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c,
-     0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64,
-     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x38, 0x0a, 0x05, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x42, 0x45, 0x47, 0x49,
-     0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x49,
-     0x53, 0x48, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x45,
-     0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x53,
-     0x49, 0x4e, 0x47, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x72, 0x67,
-     0x73, 0x22, 0xa6, 0x01, 0x0a, 0x17, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
-     0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x72, 0x6f,
-     0x70, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x15, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64,
+     0x59, 0x5f, 0x4e, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e,
+     0x54, 0x5f, 0x54, 0x41, 0x4b, 0x45, 0x53, 0x5f, 0x50, 0x52, 0x49, 0x4f,
+     0x52, 0x49, 0x54, 0x59, 0x10, 0x03, 0x22, 0x82, 0x01, 0x0a, 0x12, 0x53,
+     0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x43, 0x52,
+     0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x52, 0x5f,
+     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+     0x00, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f,
+     0x41, 0x46, 0x46, 0x45, 0x43, 0x54, 0x53, 0x5f, 0x53, 0x43, 0x52, 0x4f,
+     0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x52, 0x10, 0x01,
+     0x12, 0x29, 0x0a, 0x25, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x44,
+     0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x41, 0x46, 0x46, 0x45,
+     0x43, 0x54, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x48, 0x41,
+     0x4e, 0x44, 0x4c, 0x45, 0x52, 0x10, 0x02, 0x22, 0x8f, 0x05, 0x0a, 0x0e,
      0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
-     0x67, 0x73, 0x12, 0x52, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x62,
-     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61,
-     0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
+     0x67, 0x73, 0x12, 0x46, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67,
+     0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x41, 0x72, 0x67, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79,
+     0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+     0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73,
+     0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
+     0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x65,
+     0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+     0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69,
+     0x6d, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x73,
+     0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65,
+     0x5f, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64,
+     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x73, 0x12, 0x2a, 0x0a,
+     0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x64, 0x65,
+     0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x44, 0x65,
+     0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x6e, 0x5f,
+     0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74,
+     0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x6e, 0x43,
+     0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12,
+     0x21, 0x0a, 0x0c, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x6f,
+     0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61,
+     0x6e, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x30,
+     0x0a, 0x13, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x09, 0x20,
+     0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63,
+     0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x69, 0x64,
+     0x12, 0x4a, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c,
+     0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x6f, 0x75, 0x72,
+     0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00,
+     0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61,
+     0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x01, 0x0a, 0x12, 0x42, 0x65, 0x67,
+     0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x54,
+     0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x42, 0x45, 0x47, 0x49, 0x4e,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f,
+     0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+     0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x42, 0x45,
+     0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x52,
+     0x47, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41,
+     0x4c, 0x49, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45, 0x47,
+     0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47,
+     0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41,
+     0x4c, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x53, 0x5f,
+     0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x45, 0x44, 0x10,
+     0x03, 0x42, 0x0e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
+     0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0xf5, 0x05, 0x0a, 0x12, 0x42, 0x65,
+     0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x41, 0x72, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x75, 0x70, 0x64, 0x61,
+     0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x73, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
+     0x41, 0x74, 0x55, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x69, 0x6e, 0x69,
+     0x73, 0x68, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x73, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68,
+     0x65, 0x64, 0x41, 0x74, 0x55, 0x73, 0x12, 0x3f, 0x0a, 0x05, 0x73, 0x74,
+     0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70,
+     0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53,
+     0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
+     0x44, 0x0a, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61,
+     0x72, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
      0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74,
-     0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
-     0x67, 0x73, 0x22, 0xc5, 0x01, 0x0a, 0x15, 0x42, 0x65, 0x67, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
-     0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72,
-     0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
-     0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a,
-     0x06, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x06, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x12, 0x23, 0x0a,
-     0x0d, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65,
-     0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75,
-     0x6d, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x52,
+     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x75,
+     0x72, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x3e, 0x0a,
+     0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x05,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42,
+     0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67,
+     0x73, 0x48, 0x00, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x72, 0x67,
+     0x73, 0x12, 0x5c, 0x0a, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+     0x6d, 0x70, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75, 0x73, 0x18, 0x06, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65,
+     0x67, 0x69, 0x6e, 0x49, 0x6d, 0x70, 0x6c, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x41, 0x72, 0x67, 0x73, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+     0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x52, 0x0e, 0x74, 0x69, 0x6d,
+     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x1a,
+     0xad, 0x02, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+     0x70, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e,
+     0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x69, 0x6e, 0x74, 0x65,
+     0x72, 0x76, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x31, 0x0a,
+     0x15, 0x6e, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x61, 0x64,
+     0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6e, 0x6f, 0x77, 0x54, 0x6f, 0x44,
+     0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61,
+     0x12, 0x34, 0x0a, 0x17, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69,
+     0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x77, 0x5f, 0x64, 0x65,
+     0x6c, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4e, 0x6f,
+     0x77, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x1c, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f,
+     0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x65, 0x6c,
+     0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x44, 0x65, 0x61,
+     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x10,
+     0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x03, 0x6e, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
+     0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18,
+     0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c,
+     0x69, 0x6e, 0x65, 0x22, 0x38, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x12, 0x18, 0x0a, 0x14, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x46, 0x52,
+     0x41, 0x4d, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44,
+     0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f,
+     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x55, 0x53, 0x49, 0x4e, 0x47, 0x10,
+     0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x22, 0xa6, 0x01,
+     0x0a, 0x17, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
+     0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64,
+     0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
+     0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x15, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x42, 0x65, 0x67, 0x69,
+     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x52,
      0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e,
      0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18,
-     0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
      0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72,
      0x67, 0x73, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x42, 0x65, 0x67, 0x69,
-     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xfd,
-     0x04, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
-     0x72, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f,
-     0x72, 0x79, 0x12, 0x65, 0x0a, 0x31, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f,
-     0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x71,
-     0x75, 0x65, 0x75, 0x65, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
-     0x6c, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64,
-     0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x2a, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x43, 0x72,
-     0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61,
-     0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x6c, 0x0a,
-     0x35, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f,
-     0x6e, 0x6f, 0x74, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-     0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65,
-     0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x2d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4e, 0x6f, 0x74,
-     0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x73, 0x74, 0x69,
-     0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
-     0x76, 0x0a, 0x3b, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69,
-     0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72,
-     0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74,
-     0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x73, 0x74,
+     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xc5,
+     0x01, 0x0a, 0x15, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
+     0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x6f, 0x75,
+     0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x75,
+     0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70,
+     0x61, 0x75, 0x73, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x75, 0x6d,
+     0x5f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x62, 0x73,
+     0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x52, 0x0a, 0x15, 0x6c, 0x61,
+     0x73, 0x74, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x65, 0x67, 0x69,
+     0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x12,
+     0x6c, 0x61, 0x73, 0x74, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xfd, 0x04, 0x0a, 0x17, 0x43,
+     0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x6d,
+     0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x65,
+     0x0a, 0x31, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
+     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65,
+     0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x73,
+     0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+     0x5f, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x2a, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
+     0x61, 0x6c, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65,
+     0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x6c, 0x0a, 0x35, 0x62, 0x65, 0x67,
+     0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x61, 0x6d,
+     0x65, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f,
+     0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x73, 0x74,
      0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f,
-     0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x31, 0x62, 0x65,
+     0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x2d, 0x62, 0x65,
      0x67, 0x69, 0x6e, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79,
-     0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x45, 0x73, 0x74, 0x69,
-     0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
-     0x5d, 0x0a, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x74, 0x6f,
-     0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x63,
-     0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d,
-     0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73,
-     0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x26, 0x63, 0x6f, 0x6d, 0x6d,
-     0x69, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x41,
-     0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x45, 0x73, 0x74, 0x69, 0x6d,
-     0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x44,
-     0x0a, 0x1f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69,
-     0x6c, 0x65, 0x73, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65,
-     0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x05, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x1b, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65,
-     0x54, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74,
-     0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x3b, 0x0a, 0x1a,
-     0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x74,
-     0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f,
-     0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x61, 0x63,
-     0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61,
-     0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x33, 0x0a,
-     0x16, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61,
-     0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18,
-     0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x64, 0x72, 0x61, 0x77, 0x45,
-     0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61,
-     0x55, 0x73, 0x2a, 0xb0, 0x05, 0x0a, 0x1f, 0x43, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53,
-     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69,
-     0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
-     0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f,
-     0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
-     0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x43, 0x5f, 0x53, 0x43,
-     0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49,
-     0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x2d, 0x0a,
-     0x29, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45,
-     0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x4e,
-     0x44, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e,
-     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a,
-     0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52,
-     0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d,
-     0x49, 0x54, 0x10, 0x03, 0x12, 0x2a, 0x0a, 0x26, 0x43, 0x43, 0x5f, 0x53,
+     0x51, 0x75, 0x65, 0x75, 0x65, 0x4e, 0x6f, 0x74, 0x43, 0x72, 0x69, 0x74,
+     0x69, 0x63, 0x61, 0x6c, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65,
+     0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x76, 0x0a, 0x3b, 0x62,
+     0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x6f,
+     0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f,
+     0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74,
+     0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x31, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4d,
+     0x61, 0x69, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72,
+     0x74, 0x54, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x43, 0x6f,
+     0x6d, 0x6d, 0x69, 0x74, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65,
+     0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x5d, 0x0a, 0x2d, 0x63,
+     0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x61,
+     0x64, 0x79, 0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61,
+     0x74, 0x65, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f,
+     0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x26, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x54, 0x6f,
+     0x52, 0x65, 0x61, 0x64, 0x79, 0x54, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76,
+     0x61, 0x74, 0x65, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44,
+     0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x70, 0x72,
+     0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x5f,
+     0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c,
+     0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x1b, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6c, 0x65,
+     0x73, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65, 0x6c,
+     0x74, 0x61, 0x55, 0x73, 0x12, 0x3b, 0x0a, 0x1a, 0x61, 0x63, 0x74, 0x69,
+     0x76, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74,
+     0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x06,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61,
+     0x74, 0x65, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x44, 0x65,
+     0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x64, 0x72, 0x61,
+     0x77, 0x5f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x5f, 0x64,
+     0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x13, 0x64, 0x72, 0x61, 0x77, 0x45, 0x73, 0x74, 0x69, 0x6d,
+     0x61, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x2a, 0xb0,
+     0x05, 0x0a, 0x1f, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6d,
+     0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64,
+     0x75, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23,
+     0x0a, 0x1f, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
+     0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e,
+     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
+     0x1c, 0x0a, 0x18, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55,
+     0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e,
+     0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x2d, 0x0a, 0x29, 0x43, 0x43, 0x5f,
+     0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43,
+     0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x42, 0x45,
+     0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41,
+     0x4d, 0x45, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x43, 0x43, 0x5f, 0x53,
      0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54,
-     0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45,
-     0x5f, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x10, 0x04,
-     0x12, 0x28, 0x0a, 0x24, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44,
+     0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03,
+     0x12, 0x2a, 0x0a, 0x26, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44,
      0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
-     0x44, 0x52, 0x41, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x50, 0x4f, 0x53, 0x53,
-     0x49, 0x42, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x43,
+     0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x59, 0x4e,
+     0x43, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x10, 0x04, 0x12, 0x28, 0x0a, 0x24,
+     0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52,
+     0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57,
+     0x5f, 0x49, 0x46, 0x5f, 0x50, 0x4f, 0x53, 0x53, 0x49, 0x42, 0x4c, 0x45,
+     0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
+     0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f,
+     0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x46, 0x4f, 0x52, 0x43, 0x45,
+     0x44, 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x43, 0x43, 0x5f, 0x53, 0x43,
+     0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49,
+     0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x41, 0x42, 0x4f, 0x52,
+     0x54, 0x10, 0x07, 0x12, 0x3c, 0x0a, 0x38, 0x43, 0x43, 0x5f, 0x53, 0x43,
+     0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49,
+     0x4f, 0x4e, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4c, 0x41, 0x59,
+     0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d,
+     0x45, 0x5f, 0x53, 0x49, 0x4e, 0x4b, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54,
+     0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x25, 0x0a, 0x21, 0x43, 0x43, 0x5f,
+     0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43,
+     0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x45,
+     0x5f, 0x54, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x09, 0x12, 0x38, 0x0a, 0x34,
+     0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52,
+     0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41,
+     0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4c, 0x41, 0x59, 0x45, 0x52,
+     0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f,
+     0x53, 0x49, 0x4e, 0x4b, 0x10, 0x0a, 0x12, 0x36, 0x0a, 0x32, 0x43, 0x43,
      0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x46,
-     0x4f, 0x52, 0x43, 0x45, 0x44, 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x43,
-     0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f,
-     0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f,
-     0x41, 0x42, 0x4f, 0x52, 0x54, 0x10, 0x07, 0x12, 0x3c, 0x0a, 0x38, 0x43,
-     0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f,
-     0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e,
-     0x5f, 0x4c, 0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f,
-     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x4b, 0x5f, 0x43,
-     0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x25, 0x0a,
-     0x21, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45,
-     0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45,
-     0x50, 0x41, 0x52, 0x45, 0x5f, 0x54, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x09,
-     0x12, 0x38, 0x0a, 0x34, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44,
-     0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
-     0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4c,
-     0x41, 0x59, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x46, 0x52,
-     0x41, 0x4d, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x4b, 0x10, 0x0a, 0x12, 0x36,
-     0x0a, 0x32, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
-     0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45,
-     0x52, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x53,
-     0x49, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41,
-     0x54, 0x49, 0x4f, 0x4e, 0x10, 0x0b, 0x12, 0x42, 0x0a, 0x3e, 0x43, 0x43,
-     0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59,
-     0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f,
-     0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x58,
-     0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x54, 0x49, 0x4c,
-     0x10, 0x0c, 0x12, 0x41, 0x0a, 0x3d, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
+     0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52,
+     0x4d, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f,
+     0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e,
+     0x10, 0x0b, 0x12, 0x42, 0x0a, 0x3e, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48,
      0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f,
      0x4e, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59, 0x5f, 0x42, 0x45, 0x47,
      0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d,
      0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54,
-     0x45, 0x44, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0d, 0x42, 0x02, 0x48,
-     0x03, 0x0a, 0xb8, 0x01, 0x0a, 0x3f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72,
-     0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76,
-     0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x68,
-     0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x61, 0x6d,
-     0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x22, 0x60, 0x0a, 0x15, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
-     0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x53, 0x61, 0x6d,
-     0x70, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
-     0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08,
-     0x6e, 0x61, 0x6d, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04,
-     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-     0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x61, 0x6d, 0x70,
-     0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x61,
-     0x6d, 0x70, 0x6c, 0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x7d, 0x0a, 0x3c,
+     0x45, 0x44, 0x5f, 0x55, 0x4e, 0x54, 0x49, 0x4c, 0x10, 0x0c, 0x12, 0x41,
+     0x0a, 0x3d, 0x43, 0x43, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
+     0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f,
+     0x54, 0x49, 0x46, 0x59, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x4d,
+     0x41, 0x49, 0x4e, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x4e, 0x4f,
+     0x54, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x53,
+     0x4f, 0x4f, 0x4e, 0x10, 0x0d, 0x0a, 0xb4, 0x01, 0x0a, 0x3f, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63,
+     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f,
+     0x6d, 0x65, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d,
+     0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x60, 0x0a, 0x15, 0x43, 0x68,
+     0x72, 0x6f, 0x6d, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61,
+     0x6d, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e,
+     0x61, 0x6d, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x04, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x48, 0x61, 0x73, 0x68,
+     0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06,
+     0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x06, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x0a, 0x79, 0x0a, 0x3c,
      0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
      0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72,
      0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68,
@@ -1022,109 +1020,108 @@
      0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x43, 0x68, 0x72,
      0x6f, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76,
      0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0xbc, 0x08, 0x0a, 0x39, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
-     0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f,
-     0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65,
-     0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x69, 0x70, 0x63, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xe9,
-     0x07, 0x0a, 0x0f, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67,
-     0x61, 0x63, 0x79, 0x49, 0x70, 0x63, 0x12, 0x52, 0x0a, 0x0d, 0x6d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79,
-     0x49, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43,
-     0x6c, 0x61, 0x73, 0x73, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
-     0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
-     0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0xde, 0x06, 0x0a, 0x0c, 0x4d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x15,
-     0x0a, 0x11, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50,
-     0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a,
-     0x10, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x4d,
-     0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02,
-     0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x41,
-     0x47, 0x45, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53,
-     0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57, 0x49, 0x44, 0x47, 0x45, 0x54,
-     0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
-     0x49, 0x4e, 0x50, 0x55, 0x54, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x07, 0x12,
-     0x10, 0x0a, 0x0c, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57, 0x4f, 0x52,
-     0x4b, 0x45, 0x52, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x4e, 0x41, 0x43, 0x4c, 0x10, 0x09, 0x12, 0x15, 0x0a,
-     0x11, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x43,
-     0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10,
-     0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50,
-     0x50, 0x41, 0x50, 0x49, 0x10, 0x0c, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x10, 0x0d,
-     0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x44, 0x52,
-     0x41, 0x47, 0x10, 0x0e, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53,
-     0x53, 0x5f, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x10, 0x0f, 0x12, 0x13, 0x0a,
-     0x0f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e,
-     0x53, 0x49, 0x4f, 0x4e, 0x10, 0x10, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x49, 0x4e, 0x50,
-     0x55, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x11, 0x12,
-     0x14, 0x0a, 0x10, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x42, 0x4c, 0x49,
-     0x4e, 0x4b, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x12, 0x12, 0x17, 0x0a,
-     0x13, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53,
-     0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x13, 0x12, 0x13,
-     0x0a, 0x0f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x52,
-     0x45, 0x4e, 0x44, 0x45, 0x52, 0x10, 0x14, 0x12, 0x14, 0x0a, 0x10, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x4f, 0x54,
-     0x49, 0x4e, 0x47, 0x10, 0x15, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x42, 0x52, 0x4f, 0x57, 0x53, 0x45, 0x52, 0x5f, 0x50,
-     0x4c, 0x55, 0x47, 0x49, 0x4e, 0x10, 0x16, 0x12, 0x1a, 0x0a, 0x16, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x4e, 0x44, 0x52, 0x4f, 0x49, 0x44,
-     0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x17, 0x12,
-     0x13, 0x0a, 0x0f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4e, 0x41, 0x43,
-     0x4c, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x18, 0x12, 0x19, 0x0a, 0x15,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,
-     0x54, 0x45, 0x44, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x19, 0x12,
-     0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x43, 0x41, 0x53,
-     0x54, 0x10, 0x1a, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x4c, 0x41, 0x53, 0x53,
-     0x5f, 0x47, 0x49, 0x4e, 0x5f, 0x4a, 0x41, 0x56, 0x41, 0x5f, 0x42, 0x52,
-     0x49, 0x44, 0x47, 0x45, 0x10, 0x1b, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x5f, 0x55,
-     0x54, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x4e, 0x54,
-     0x49, 0x4e, 0x47, 0x10, 0x1c, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x4f, 0x5a, 0x4f, 0x4e, 0x45, 0x5f, 0x47, 0x50, 0x55,
-     0x10, 0x1d, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
-     0x57, 0x45, 0x42, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x1e, 0x12, 0x17,
-     0x0a, 0x13, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4e, 0x45, 0x54, 0x57,
-     0x4f, 0x52, 0x4b, 0x5f, 0x48, 0x49, 0x4e, 0x54, 0x53, 0x10, 0x1f, 0x12,
-     0x1f, 0x0a, 0x1b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x54,
-     0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x47, 0x55, 0x45, 0x53,
-     0x54, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x20, 0x12, 0x14, 0x0a, 0x10,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54, 0x5f,
-     0x56, 0x49, 0x45, 0x57, 0x10, 0x21, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x4c,
-     0x41, 0x59, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, 0x54,
-     0x45, 0x10, 0x22, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4c, 0x41, 0x53, 0x53,
-     0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x57,
-     0x4f, 0x52, 0x4b, 0x45, 0x52, 0x10, 0x23, 0x12, 0x1c, 0x0a, 0x18, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x53, 0x55, 0x42, 0x52, 0x45, 0x53, 0x4f,
-     0x55, 0x52, 0x43, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x10,
-     0x24, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x55,
-     0x4e, 0x46, 0x52, 0x45, 0x45, 0x5a, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46,
-     0x52, 0x41, 0x4d, 0x45, 0x10, 0x25, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x7b,
-     0x0a, 0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f,
-     0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f,
-     0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f,
-     0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
-     0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x22, 0x29, 0x0a, 0x0f, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12,
-     0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0x81, 0x18, 0x0a, 0x33, 0x70, 0x72, 0x6f, 0x74,
+     0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x0a,
+     0xb8, 0x08, 0x0a, 0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63,
+     0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e,
+     0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6c, 0x65, 0x67,
+     0x61, 0x63, 0x79, 0x5f, 0x69, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xe9, 0x07, 0x0a, 0x0f, 0x43,
+     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49,
+     0x70, 0x63, 0x12, 0x52, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+     0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x0e, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f,
+     0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49, 0x70, 0x63, 0x2e,
+     0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73,
+     0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61,
+     0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+     0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
+     0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x6e,
+     0x65, 0x22, 0xde, 0x06, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
+     0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4c,
+     0x41, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
+     0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4c, 0x41,
+     0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x4d, 0x41, 0x54, 0x49, 0x4f,
+     0x4e, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53,
+     0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a,
+     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x10, 0x03,
+     0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x56, 0x49,
+     0x45, 0x57, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4c, 0x41, 0x53,
+     0x53, 0x5f, 0x57, 0x49, 0x44, 0x47, 0x45, 0x54, 0x10, 0x05, 0x12, 0x0f,
+     0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x49, 0x4e, 0x50, 0x55,
+     0x54, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53,
+     0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x43,
+     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x45, 0x52, 0x10,
+     0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4e,
+     0x41, 0x43, 0x4c, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4c, 0x41,
+     0x53, 0x53, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e,
+     0x45, 0x4c, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53,
+     0x53, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x0b, 0x12, 0x0f, 0x0a,
+     0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x50, 0x41, 0x50, 0x49,
+     0x10, 0x0c, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
+     0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x10, 0x0d, 0x12, 0x0e, 0x0a, 0x0a,
+     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x44, 0x52, 0x41, 0x47, 0x10, 0x0e,
+     0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x52,
+     0x49, 0x4e, 0x54, 0x10, 0x0f, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4c, 0x41,
+     0x53, 0x53, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e,
+     0x10, 0x10, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
+     0x54, 0x45, 0x58, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x43,
+     0x4c, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x11, 0x12, 0x14, 0x0a, 0x10, 0x43,
+     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54,
+     0x45, 0x53, 0x54, 0x10, 0x12, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4c, 0x41,
+     0x53, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x49, 0x42, 0x49,
+     0x4c, 0x49, 0x54, 0x59, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4c,
+     0x41, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x52, 0x45, 0x4e, 0x44, 0x45,
+     0x52, 0x10, 0x14, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4c, 0x41, 0x53, 0x53,
+     0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x10,
+     0x15, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x42,
+     0x52, 0x4f, 0x57, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x4c, 0x55, 0x47, 0x49,
+     0x4e, 0x10, 0x16, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4c, 0x41, 0x53, 0x53,
+     0x5f, 0x41, 0x4e, 0x44, 0x52, 0x4f, 0x49, 0x44, 0x5f, 0x57, 0x45, 0x42,
+     0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x17, 0x12, 0x13, 0x0a, 0x0f, 0x43,
+     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4e, 0x41, 0x43, 0x4c, 0x5f, 0x48, 0x4f,
+     0x53, 0x54, 0x10, 0x18, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x4c, 0x41, 0x53,
+     0x53, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x45, 0x44, 0x5f,
+     0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x19, 0x12, 0x0e, 0x0a, 0x0a, 0x43,
+     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x43, 0x41, 0x53, 0x54, 0x10, 0x1a, 0x12,
+     0x19, 0x0a, 0x15, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x47, 0x49, 0x4e,
+     0x5f, 0x4a, 0x41, 0x56, 0x41, 0x5f, 0x42, 0x52, 0x49, 0x44, 0x47, 0x45,
+     0x10, 0x1b, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
+     0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x54, 0x49, 0x4c, 0x49,
+     0x54, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x49, 0x4e, 0x47, 0x10,
+     0x1c, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4f,
+     0x5a, 0x4f, 0x4e, 0x45, 0x5f, 0x47, 0x50, 0x55, 0x10, 0x1d, 0x12, 0x12,
+     0x0a, 0x0e, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57, 0x45, 0x42, 0x5f,
+     0x54, 0x45, 0x53, 0x54, 0x10, 0x1e, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4c,
+     0x41, 0x53, 0x53, 0x5f, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f,
+     0x48, 0x49, 0x4e, 0x54, 0x53, 0x10, 0x1f, 0x12, 0x1f, 0x0a, 0x1b, 0x43,
+     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49,
+     0x4f, 0x4e, 0x53, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x56, 0x49,
+     0x45, 0x57, 0x10, 0x20, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4c, 0x41, 0x53,
+     0x53, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x56, 0x49, 0x45, 0x57,
+     0x10, 0x21, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
+     0x4d, 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x4c, 0x41, 0x59, 0x45, 0x52,
+     0x5f, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, 0x54, 0x45, 0x10, 0x22, 0x12,
+     0x1a, 0x0a, 0x16, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x54,
+     0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x45,
+     0x52, 0x10, 0x23, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4c, 0x41, 0x53, 0x53,
+     0x5f, 0x53, 0x55, 0x42, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45,
+     0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x24, 0x12, 0x1b, 0x0a,
+     0x17, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x46, 0x52, 0x45,
+     0x45, 0x5a, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45,
+     0x10, 0x25, 0x0a, 0x77, 0x0a, 0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72,
+     0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76,
+     0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x75,
+     0x73, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x29, 0x0a, 0x0f,
+     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x45, 0x76,
+     0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f,
+     0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74,
+     0x69, 0x6f, 0x6e, 0x0a, 0xfd, 0x17, 0x0a, 0x33, 0x70, 0x72, 0x6f, 0x74,
      0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
      0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f,
      0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f,
@@ -1380,7 +1377,7 @@
      0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
      0x28, 0x04, 0x52, 0x03, 0x69, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
      0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
-     0x61, 0x6d, 0x65, 0x42, 0x02, 0x48, 0x03}};
+     0x61, 0x6d, 0x65}};
 
 }  // namespace perfetto
 
diff --git a/src/trace_processor/importers/proto/track_event_module.cc b/src/trace_processor/importers/proto/track_event_module.cc
index c731fcc..1bbbebd 100644
--- a/src/trace_processor/importers/proto/track_event_module.cc
+++ b/src/trace_processor/importers/proto/track_event_module.cc
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 #include "src/trace_processor/importers/proto/track_event_module.h"
-#include "perfetto/base/build_config.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
+#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/track_tracker.h"
+
+#include "protos/perfetto/config/data_source_config.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -43,20 +48,15 @@
     uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kTrackDescriptorFieldNumber:
-      tokenizer_.TokenizeTrackDescriptorPacket(decoder);
-      return ModuleResult::Handled();
+      return tokenizer_.TokenizeTrackDescriptorPacket(state, decoder,
+                                                      packet_timestamp);
     case TracePacket::kTrackEventFieldNumber:
       tokenizer_.TokenizeTrackEventPacket(state, decoder, packet,
                                           packet_timestamp);
       return ModuleResult::Handled();
     case TracePacket::kThreadDescriptorFieldNumber:
-      // TODO(eseckler): Remove these once Chrome has switched fully over to
-      // TrackDescriptors.
-      tokenizer_.TokenizeThreadDescriptorPacket(state, decoder);
-      return ModuleResult::Handled();
-    case TracePacket::kProcessDescriptorFieldNumber:
-      tokenizer_.TokenizeProcessDescriptorPacket(decoder);
-      return ModuleResult::Handled();
+      // TODO(eseckler): Remove once Chrome has switched to TrackDescriptors.
+      return tokenizer_.TokenizeThreadDescriptorPacket(state, decoder);
   }
   return ModuleResult::Ignored();
 }
@@ -64,12 +64,28 @@
 void TrackEventModule::ParsePacket(const TracePacket::Decoder& decoder,
                                    const TimestampedTracePiece& ttp,
                                    uint32_t field_id) {
-  if (field_id == TracePacket::kTrackEventFieldNumber) {
-    PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTrackEvent);
-    parser_.ParseTrackEvent(
-        ttp.timestamp, ttp.track_event_data->thread_timestamp,
-        ttp.track_event_data->thread_instruction_count,
-        ttp.track_event_data->sequence_state, decoder.track_event());
+  switch (field_id) {
+    case TracePacket::kTrackDescriptorFieldNumber:
+      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
+      parser_.ParseTrackDescriptor(decoder.track_descriptor());
+      break;
+    case TracePacket::kTrackEventFieldNumber:
+      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTrackEvent);
+      parser_.ParseTrackEvent(
+          ttp.timestamp, ttp.track_event_data->thread_timestamp,
+          ttp.track_event_data->thread_instruction_count,
+          ttp.track_event_data->sequence_state, decoder.track_event());
+      break;
+    case TracePacket::kProcessDescriptorFieldNumber:
+      // TODO(eseckler): Remove once Chrome has switched to TrackDescriptors.
+      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
+      parser_.ParseProcessDescriptor(decoder.process_descriptor());
+      break;
+    case TracePacket::kThreadDescriptorFieldNumber:
+      // TODO(eseckler): Remove once Chrome has switched to TrackDescriptors.
+      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
+      parser_.ParseThreadDescriptor(decoder.thread_descriptor());
+      break;
   }
 }
 
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index f631b6b..fc46e7b 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -31,11 +31,16 @@
 #include "protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_keyed_service.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_user_event.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/log_message.pbzero.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "protos/perfetto/trace/track_event/task_execution.pbzero.h"
+#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
 namespace perfetto {
@@ -109,8 +114,8 @@
           context->storage->InternString("track_event.log_message")),
       raw_legacy_event_id_(
           context->storage->InternString("track_event.legacy_event")),
-      legacy_event_original_tid_id_(
-          context->storage->InternString("legacy_event.original_tid")),
+      legacy_event_passthrough_utid_id_(
+          context->storage->InternString("legacy_event.passthrough_utid")),
       legacy_event_category_key_id_(
           context->storage->InternString("legacy_event.category")),
       legacy_event_name_key_id_(
@@ -200,7 +205,141 @@
            context->storage->InternString("MEDIA_PLAYER_DELEGATE"),
            context->storage->InternString("EXTENSION_WORKER"),
            context->storage->InternString("SUBRESOURCE_FILTER"),
-           context->storage->InternString("UNFREEZABLE_FRAME")}} {}
+           context->storage->InternString("UNFREEZABLE_FRAME")}},
+      chrome_process_name_ids_{
+          {kNullStringId, context_->storage->InternString("Browser"),
+           context_->storage->InternString("Renderer"),
+           context_->storage->InternString("Utility"),
+           context_->storage->InternString("Zygote"),
+           context_->storage->InternString("SandboxHelper"),
+           context_->storage->InternString("Gpu"),
+           context_->storage->InternString("PpapiPlugin"),
+           context_->storage->InternString("PpapiBroker")}},
+      chrome_thread_name_ids_{
+          {kNullStringId, context_->storage->InternString("CrProcessMain"),
+           context_->storage->InternString("ChromeIOThread"),
+           context_->storage->InternString("ThreadPoolBackgroundWorker&"),
+           context_->storage->InternString("ThreadPoolForegroundWorker&"),
+           context_->storage->InternString(
+               "ThreadPoolSingleThreadForegroundBlocking&"),
+           context_->storage->InternString(
+               "ThreadPoolSingleThreadBackgroundBlocking&"),
+           context_->storage->InternString("ThreadPoolService"),
+           context_->storage->InternString("Compositor"),
+           context_->storage->InternString("VizCompositorThread"),
+           context_->storage->InternString("CompositorTileWorker&"),
+           context_->storage->InternString("ServiceWorkerThread&"),
+           context_->storage->InternString("MemoryInfra"),
+           context_->storage->InternString("StackSamplingProfiler")}} {}
+
+void TrackEventParser::ParseTrackDescriptor(
+    protozero::ConstBytes track_descriptor) {
+  protos::pbzero::TrackDescriptor::Decoder decoder(track_descriptor);
+
+  // Ensure that the track and its parents are resolved. This may start a new
+  // process and/or thread (i.e. new upid/utid).
+  TrackId track_id =
+      *context_->track_tracker->GetDescriptorTrack(decoder.uuid());
+
+  if (decoder.has_process()) {
+    UniquePid upid = ParseProcessDescriptor(decoder.process());
+    if (decoder.has_chrome_process())
+      ParseChromeProcessDescriptor(upid, decoder.chrome_process());
+  }
+
+  if (decoder.has_thread()) {
+    UniqueTid utid = ParseThreadDescriptor(decoder.thread());
+    if (decoder.has_chrome_thread())
+      ParseChromeThreadDescriptor(utid, decoder.chrome_thread());
+  }
+
+  if (decoder.has_name()) {
+    auto* tracks = context_->storage->mutable_track_table();
+    StringId name_id = context_->storage->InternString(decoder.name());
+    tracks->mutable_name()->Set(*tracks->id().IndexOf(track_id), name_id);
+  }
+}
+
+UniquePid TrackEventParser::ParseProcessDescriptor(
+    protozero::ConstBytes process_descriptor) {
+  protos::pbzero::ProcessDescriptor::Decoder decoder(process_descriptor);
+  UniquePid upid = context_->process_tracker->GetOrCreateProcess(
+      static_cast<uint32_t>(decoder.pid()));
+  if (decoder.has_process_name() && decoder.process_name().size) {
+    // Don't override system-provided names.
+    context_->process_tracker->SetProcessNameIfUnset(
+        upid, context_->storage->InternString(decoder.process_name()));
+  }
+  // TODO(skyostil): Remove parsing for legacy chrome_process_type field.
+  if (decoder.has_chrome_process_type()) {
+    auto process_type = decoder.chrome_process_type();
+    size_t name_index =
+        static_cast<size_t>(process_type) < chrome_process_name_ids_.size()
+            ? static_cast<size_t>(process_type)
+            : 0u;
+    StringId name_id = chrome_process_name_ids_[name_index];
+    // Don't override system-provided names.
+    context_->process_tracker->SetProcessNameIfUnset(upid, name_id);
+  }
+  return upid;
+}
+
+void TrackEventParser::ParseChromeProcessDescriptor(
+    UniquePid upid,
+    protozero::ConstBytes chrome_process_descriptor) {
+  protos::pbzero::ChromeProcessDescriptor::Decoder decoder(
+      chrome_process_descriptor);
+  auto process_type = decoder.process_type();
+  size_t name_index =
+      static_cast<size_t>(process_type) < chrome_process_name_ids_.size()
+          ? static_cast<size_t>(process_type)
+          : 0u;
+  StringId name_id = chrome_process_name_ids_[name_index];
+  // Don't override system-provided names.
+  context_->process_tracker->SetProcessNameIfUnset(upid, name_id);
+}
+
+UniqueTid TrackEventParser::ParseThreadDescriptor(
+    protozero::ConstBytes thread_descriptor) {
+  protos::pbzero::ThreadDescriptor::Decoder decoder(thread_descriptor);
+  UniqueTid utid = context_->process_tracker->UpdateThread(
+      static_cast<uint32_t>(decoder.tid()),
+      static_cast<uint32_t>(decoder.pid()));
+  StringId name_id = kNullStringId;
+  if (decoder.has_thread_name() && decoder.thread_name().size) {
+    name_id = context_->storage->InternString(decoder.thread_name());
+  } else if (decoder.has_chrome_thread_type()) {
+    // TODO(skyostil): Remove parsing for legacy chrome_thread_type field.
+    auto thread_type = decoder.chrome_thread_type();
+    size_t name_index =
+        static_cast<size_t>(thread_type) < chrome_thread_name_ids_.size()
+            ? static_cast<size_t>(thread_type)
+            : 0u;
+    name_id = chrome_thread_name_ids_[name_index];
+  }
+  if (name_id != kNullStringId) {
+    // Don't override system-provided names.
+    context_->process_tracker->SetThreadNameIfUnset(utid, name_id);
+  }
+  return utid;
+}
+
+void TrackEventParser::ParseChromeThreadDescriptor(
+    UniqueTid utid,
+    protozero::ConstBytes chrome_thread_descriptor) {
+  protos::pbzero::ChromeThreadDescriptor::Decoder decoder(
+      chrome_thread_descriptor);
+  if (!decoder.has_thread_type())
+    return;
+
+  auto thread_type = decoder.thread_type();
+  size_t name_index =
+      static_cast<size_t>(thread_type) < chrome_thread_name_ids_.size()
+          ? static_cast<size_t>(thread_type)
+          : 0u;
+  StringId name_id = chrome_thread_name_ids_[name_index];
+  context_->process_tracker->SetThreadNameIfUnset(utid, name_id);
+}
 
 void TrackEventParser::ParseTrackEvent(
     int64_t ts,
@@ -252,7 +391,7 @@
     category_strings.push_back(*it);
   }
 
-  StringId category_id = 0;
+  StringId category_id = kNullStringId;
 
   // If there's a single category, we can avoid building a concatenated
   // string.
@@ -289,7 +428,7 @@
       category_id = storage->InternString(base::StringView(categories));
   }
 
-  StringId name_id = 0;
+  StringId name_id = kNullStringId;
 
   uint64_t name_iid = event.name_iid();
   if (!name_iid)
@@ -317,9 +456,19 @@
   base::Optional<UniqueTid> utid;
   base::Optional<UniqueTid> upid;
 
+  // All events in legacy JSON require a thread ID, but for some types of events
+  // (e.g. async events or process/global-scoped instants), we don't store it in
+  // the slice/track model. To pass the utid through to the json export, we
+  // store it in an arg.
+  base::Optional<UniqueTid> legacy_passthrough_utid;
+
   // Determine track from track_uuid specified in either TrackEvent or
-  // TrackEventDefaults. If none is set, fall back to the track specified by the
-  // sequence's (or event's) pid + tid or a default track.
+  // TrackEventDefaults. If a non-default track is not set, we either:
+  //   a) fall back to the track specified by the sequence's (or event's) pid +
+  //      tid (only in case of legacy tracks/events, i.e. events that don't
+  //      specify an explicit track uuid or use legacy event phases instead of
+  //      TrackEvent types), or
+  //   b) a default track.
   if (track_uuid) {
     base::Optional<TrackId> opt_track_id =
         track_tracker->GetDescriptorTrack(track_uuid);
@@ -334,16 +483,25 @@
         context_->storage->thread_track_table().id().IndexOf(track_id);
     if (thread_track_row) {
       utid = storage->thread_track_table().utid()[*thread_track_row];
-      upid = storage->GetThread(*utid).upid;
+      upid = storage->thread_table().upid()[*utid];
     } else {
       auto process_track_row =
           context_->storage->process_track_table().id().IndexOf(track_id);
-      if (process_track_row)
+      if (process_track_row) {
         upid = storage->process_track_table().upid()[*process_track_row];
+        if (sequence_state->state()->pid_and_tid_valid()) {
+          uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
+          uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
+          UniqueTid utid_candidate = procs->UpdateThread(tid, pid);
+          if (storage->thread_table().upid()[utid_candidate] == upid)
+            legacy_passthrough_utid = utid_candidate;
+        }
+      }
     }
-  } else if (sequence_state->state()->pid_and_tid_valid() ||
-             (legacy_event.has_pid_override() &&
-              legacy_event.has_tid_override())) {
+  } else if ((!event.has_track_uuid() || !event.has_type()) &&
+             (sequence_state->state()->pid_and_tid_valid() ||
+              (legacy_event.has_pid_override() &&
+               legacy_event.has_tid_override()))) {
     uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
     uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
     if (legacy_event.has_pid_override())
@@ -352,18 +510,12 @@
       tid = static_cast<uint32_t>(legacy_event.tid_override());
 
     utid = procs->UpdateThread(tid, pid);
-    upid = storage->GetThread(*utid).upid;
-    track_id = track_tracker->GetOrCreateDescriptorTrackForThread(*utid);
+    upid = storage->thread_table().upid()[*utid];
+    track_id = track_tracker->InternThreadTrack(*utid);
   } else {
     track_id = track_tracker->GetOrCreateDefaultDescriptorTrack();
   }
 
-  // All events in legacy JSON require a thread ID, but for some types of events
-  // (e.g. async events or process/global-scoped instants), we don't store it in
-  // the slice/track model. To pass the original tid through to the json export,
-  // we store it in an arg.
-  uint32_t legacy_tid = 0;
-
   // TODO(eseckler): Replace phase with type and remove handling of
   // legacy_event.phase() once it is no longer used by producers.
   int32_t phase = 0;
@@ -409,8 +561,7 @@
         track_id = context_->track_tracker->InternLegacyChromeAsyncTrack(
             name_id, upid ? *upid : 0, source_id, source_id_is_process_scoped,
             id_scope);
-        if (utid)
-          legacy_tid = storage->GetThread(*utid).tid;
+        legacy_passthrough_utid = utid;
         break;
       }
       case 'i':
@@ -431,8 +582,7 @@
           case LegacyEvent::SCOPE_GLOBAL:
             track_id = context_->track_tracker
                            ->GetOrCreateLegacyChromeGlobalInstantTrack();
-            if (utid)
-              legacy_tid = storage->GetThread(*utid).tid;
+            legacy_passthrough_utid = utid;
             break;
           case LegacyEvent::SCOPE_PROCESS:
             if (!upid) {
@@ -445,8 +595,7 @@
             track_id =
                 context_->track_tracker->InternLegacyChromeProcessInstantTrack(
                     *upid);
-            if (utid)
-              legacy_tid = storage->GetThread(*utid).tid;
+            legacy_passthrough_utid = utid;
             break;
         }
         break;
@@ -472,7 +621,8 @@
   }
 
   auto args_callback = [this, &event, &legacy_event, sequence_state, ts, utid,
-                        legacy_tid](ArgsTracker::BoundInserter* inserter) {
+                        legacy_passthrough_utid](
+                           ArgsTracker::BoundInserter* inserter) {
     for (auto it = event.debug_annotations(); it; ++it) {
       ParseDebugAnnotationArgs(*it, sequence_state, inserter);
     }
@@ -500,9 +650,9 @@
       ParseChromeHistogramSample(event.chrome_histogram_sample(), inserter);
     }
 
-    if (legacy_tid) {
-      inserter->AddArg(legacy_event_original_tid_id_,
-                       Variadic::Integer(static_cast<int32_t>(legacy_tid)));
+    if (legacy_passthrough_utid) {
+      inserter->AddArg(legacy_event_passthrough_utid_id_,
+                       Variadic::UnsignedInteger(*legacy_passthrough_utid));
     }
 
     // TODO(eseckler): Parse legacy flow events into flow events table once we
@@ -756,14 +906,14 @@
     return;
   }
 
-  uint32_t row = context_->storage->mutable_raw_events()->AddRawEvent(
-      ts, raw_legacy_event_id_, 0, *utid);
+  RawId id = context_->storage->mutable_raw_table()->Insert(
+      {ts, raw_legacy_event_id_, 0, *utid});
 
   ArgsTracker args(context_);
-  ArgsTracker::BoundInserter inserter(&args, TableId::kRawEvents, row);
+  auto inserter = args.AddArgsTo(id);
 
-  inserter.AddArg(legacy_event_category_key_id_, Variadic::String(category_id));
-  inserter.AddArg(legacy_event_name_key_id_, Variadic::String(name_id));
+  inserter.AddArg(legacy_event_category_key_id_, Variadic::String(category_id))
+      .AddArg(legacy_event_name_key_id_, Variadic::String(name_id));
 
   std::string phase_string(1, static_cast<char>(legacy_event.phase()));
   StringId phase_id = context_->storage->InternString(phase_string.c_str());
@@ -837,7 +987,7 @@
   protos::pbzero::DebugAnnotation::Decoder annotation(debug_annotation.data,
                                                       debug_annotation.size);
 
-  StringId name_id = 0;
+  StringId name_id = kNullStringId;
 
   uint64_t name_iid = annotation.name_iid();
   if (PERFETTO_LIKELY(name_iid)) {
@@ -954,8 +1104,8 @@
   if (!decoder)
     return;
 
-  StringId file_name_id = 0;
-  StringId function_name_id = 0;
+  StringId file_name_id = kNullStringId;
+  StringId function_name_id = kNullStringId;
   uint32_t line_number = 0;
 
   TraceStorage* storage = context_->storage.get();
@@ -987,7 +1137,7 @@
 
   TraceStorage* storage = context_->storage.get();
 
-  StringId log_message_id = 0;
+  StringId log_message_id = kNullStringId;
 
   auto* decoder = sequence_state->LookupInternedMessage<
       protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
@@ -1002,8 +1152,8 @@
   context_->storage->mutable_android_log_table()->Insert(
       {ts, *utid,
        /*priority*/ 0,
-       /*tag_id*/ 0,  // TODO(nicomazz): Abuse tag_id to display
-                      // "file_name:line_number".
+       /*tag_id*/ kNullStringId,  // TODO(nicomazz): Abuse tag_id to display
+                                  // "file_name:line_number".
        log_message_id});
 
   inserter->AddArg(log_message_body_key_id_, Variadic::String(log_message_id));
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index a0a42c7..10d8e03 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -39,11 +39,19 @@
  public:
   explicit TrackEventParser(TraceProcessorContext* context);
 
+  void ParseTrackDescriptor(protozero::ConstBytes);
+  UniquePid ParseProcessDescriptor(protozero::ConstBytes);
+  UniqueTid ParseThreadDescriptor(protozero::ConstBytes);
+
   void ParseTrackEvent(int64_t ts,
                        int64_t tts,
                        int64_t ticount,
                        PacketSequenceStateGeneration*,
                        protozero::ConstBytes);
+
+ private:
+  void ParseChromeProcessDescriptor(UniquePid upid, protozero::ConstBytes);
+  void ParseChromeThreadDescriptor(UniqueTid utid, protozero::ConstBytes);
   void ParseLegacyEventAsRawEvent(
       int64_t ts,
       int64_t tts,
@@ -80,7 +88,6 @@
   void ParseChromeHistogramSample(protozero::ConstBytes chrome_keyed_service,
                                   ArgsTracker::BoundInserter* inserter);
 
- private:
   TraceProcessorContext* context_;
 
   const StringId task_file_name_args_key_id_;
@@ -88,7 +95,7 @@
   const StringId task_line_number_args_key_id_;
   const StringId log_message_body_key_id_;
   const StringId raw_legacy_event_id_;
-  const StringId legacy_event_original_tid_id_;
+  const StringId legacy_event_passthrough_utid_id_;
   const StringId legacy_event_category_key_id_;
   const StringId legacy_event_name_key_id_;
   const StringId legacy_event_phase_key_id_;
@@ -117,6 +124,8 @@
   const StringId chrome_histogram_sample_sample_args_key_id_;
 
   std::array<StringId, 38> chrome_legacy_ipc_class_ids_;
+  std::array<StringId, 9> chrome_process_name_ids_;
+  std::array<StringId, 14> chrome_thread_name_ids_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/track_event_tokenizer.cc b/src/trace_processor/importers/proto/track_event_tokenizer.cc
index 44f0750..ebd1391 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.cc
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.cc
@@ -41,63 +41,20 @@
 namespace trace_processor {
 
 TrackEventTokenizer::TrackEventTokenizer(TraceProcessorContext* context)
-    : context_(context),
-      process_name_ids_{{context_->storage->InternString("Unknown"),
-                         context_->storage->InternString("Browser"),
-                         context_->storage->InternString("Renderer"),
-                         context_->storage->InternString("Utility"),
-                         context_->storage->InternString("Zygote"),
-                         context_->storage->InternString("SandboxHelper"),
-                         context_->storage->InternString("Gpu"),
-                         context_->storage->InternString("PpapiPlugin"),
-                         context_->storage->InternString("PpapiBroker")}} {}
+    : context_(context) {}
 
-void TrackEventTokenizer::TokenizeTrackDescriptorPacket(
-    const protos::pbzero::TracePacket::Decoder& packet_decoder) {
+ModuleResult TrackEventTokenizer::TokenizeTrackDescriptorPacket(
+    PacketSequenceState* state,
+    const protos::pbzero::TracePacket::Decoder& packet_decoder,
+    int64_t packet_timestamp) {
   auto track_descriptor_field = packet_decoder.track_descriptor();
   protos::pbzero::TrackDescriptor::Decoder track_descriptor_decoder(
       track_descriptor_field.data, track_descriptor_field.size);
 
   if (!track_descriptor_decoder.has_uuid()) {
-    PERFETTO_ELOG("TrackDescriptor packet without trusted_packet_sequence_id");
+    PERFETTO_ELOG("TrackDescriptor packet without uuid");
     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
-    return;
-  }
-
-  base::Optional<UniquePid> upid;
-  base::Optional<UniqueTid> utid;
-
-  if (track_descriptor_decoder.has_process()) {
-    auto process_descriptor_field = track_descriptor_decoder.process();
-    protos::pbzero::ProcessDescriptor::Decoder process_descriptor_decoder(
-        process_descriptor_field.data, process_descriptor_field.size);
-
-    // TODO(eseckler): Also parse process name / type here.
-
-    upid = context_->process_tracker->GetOrCreateProcess(
-        static_cast<uint32_t>(process_descriptor_decoder.pid()));
-
-    if (track_descriptor_decoder.has_chrome_process()) {
-      auto chrome_process_descriptor_field =
-          track_descriptor_decoder.chrome_process();
-      protos::pbzero::ChromeProcessDescriptor::Decoder
-          chrome_process_descriptor_decoder(
-              chrome_process_descriptor_field.data,
-              chrome_process_descriptor_field.size);
-
-      auto process_type = chrome_process_descriptor_decoder.process_type();
-      size_t name_index =
-          static_cast<size_t>(process_type) < process_name_ids_.size()
-              ? static_cast<size_t>(process_type)
-              : 0u;
-      StringId name = process_name_ids_[name_index];
-
-      // Don't override system-provided names.
-      context_->process_tracker->SetProcessNameIfUnset(
-          context_->process_tracker->GetOrCreateProcess(
-              static_cast<uint32_t>(process_descriptor_decoder.pid())),
-          name);
-    }
+    return ModuleResult::Handled();
   }
 
   if (track_descriptor_decoder.has_thread()) {
@@ -105,61 +62,57 @@
     protos::pbzero::ThreadDescriptor::Decoder thread_descriptor_decoder(
         thread_descriptor_field.data, thread_descriptor_field.size);
 
-    TokenizeThreadDescriptor(thread_descriptor_decoder);
-    utid = context_->process_tracker->UpdateThread(
-        static_cast<uint32_t>(thread_descriptor_decoder.tid()),
-        static_cast<uint32_t>(thread_descriptor_decoder.pid()));
-    upid = *context_->storage->GetThread(*utid).upid;
-
-    if (track_descriptor_decoder.has_chrome_thread()) {
-      auto chrome_thread_descriptor_field =
-          track_descriptor_decoder.chrome_thread();
-      protos::pbzero::ChromeThreadDescriptor::Decoder
-          chrome_thread_descriptor_decoder(chrome_thread_descriptor_field.data,
-                                           chrome_thread_descriptor_field.size);
-
-      TokenizeChromeThreadDescriptor(thread_descriptor_decoder.pid(),
-                                     thread_descriptor_decoder.tid(),
-                                     chrome_thread_descriptor_decoder);
+    if (!thread_descriptor_decoder.has_pid() ||
+        !thread_descriptor_decoder.has_tid()) {
+      PERFETTO_ELOG(
+          "No pid or tid in ThreadDescriptor for track with uuid %" PRIu64,
+          track_descriptor_decoder.uuid());
+      context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+      return ModuleResult::Handled();
     }
+
+    if (state->IsIncrementalStateValid()) {
+      TokenizeThreadDescriptor(state, thread_descriptor_decoder);
+    }
+
+    context_->track_tracker->ReserveDescriptorThreadTrack(
+        track_descriptor_decoder.uuid(), track_descriptor_decoder.parent_uuid(),
+        static_cast<uint32_t>(thread_descriptor_decoder.pid()),
+        static_cast<uint32_t>(thread_descriptor_decoder.tid()),
+        packet_timestamp);
+  } else if (track_descriptor_decoder.has_process()) {
+    auto process_descriptor_field = track_descriptor_decoder.process();
+    protos::pbzero::ProcessDescriptor::Decoder process_descriptor_decoder(
+        process_descriptor_field.data, process_descriptor_field.size);
+
+    if (!process_descriptor_decoder.has_pid()) {
+      PERFETTO_ELOG("No pid in ProcessDescriptor for track with uuid %" PRIu64,
+                    track_descriptor_decoder.uuid());
+      context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+      return ModuleResult::Handled();
+    }
+
+    context_->track_tracker->ReserveDescriptorProcessTrack(
+        track_descriptor_decoder.uuid(),
+        static_cast<uint32_t>(process_descriptor_decoder.pid()),
+        packet_timestamp);
+  } else {
+    context_->track_tracker->ReserveDescriptorChildTrack(
+        track_descriptor_decoder.uuid(),
+        track_descriptor_decoder.parent_uuid());
   }
 
-  StringId name_id =
-      context_->storage->InternString(track_descriptor_decoder.name());
-
-  context_->track_tracker->UpdateDescriptorTrack(
-      track_descriptor_decoder.uuid(), name_id, upid, utid);
+  // Let ProtoTraceTokenizer forward the packet to the parser.
+  return ModuleResult::Ignored();
 }
 
-void TrackEventTokenizer::TokenizeProcessDescriptorPacket(
-    const protos::pbzero::TracePacket::Decoder& packet_decoder) {
-  protos::pbzero::ProcessDescriptor::Decoder process_descriptor_decoder(
-      packet_decoder.process_descriptor());
-  // TODO(skyostil): Remove parsing for legacy chrome_process_type field.
-  if (!process_descriptor_decoder.has_chrome_process_type())
-    return;
-
-  auto process_type = process_descriptor_decoder.chrome_process_type();
-  size_t name_index =
-      static_cast<size_t>(process_type) < process_name_ids_.size()
-          ? static_cast<size_t>(process_type)
-          : 0u;
-  StringId name = process_name_ids_[name_index];
-
-  // Don't override system-provided names.
-  context_->process_tracker->SetProcessNameIfUnset(
-      context_->process_tracker->GetOrCreateProcess(
-          static_cast<uint32_t>(process_descriptor_decoder.pid())),
-      name);
-}
-
-void TrackEventTokenizer::TokenizeThreadDescriptorPacket(
+ModuleResult TrackEventTokenizer::TokenizeThreadDescriptorPacket(
     PacketSequenceState* state,
     const protos::pbzero::TracePacket::Decoder& packet_decoder) {
   if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) {
     PERFETTO_ELOG("ThreadDescriptor packet without trusted_packet_sequence_id");
     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
-    return;
+    return ModuleResult::Handled();
   }
 
   // TrackEvents will be ignored while incremental state is invalid. As a
@@ -169,155 +122,29 @@
   // the first subsequent descriptor after incremental state is cleared.
   if (!state->IsIncrementalStateValid()) {
     context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
-    return;
+    return ModuleResult::Handled();
   }
 
   auto thread_descriptor_field = packet_decoder.thread_descriptor();
   protos::pbzero::ThreadDescriptor::Decoder thread_descriptor_decoder(
       thread_descriptor_field.data, thread_descriptor_field.size);
+  TokenizeThreadDescriptor(state, thread_descriptor_decoder);
 
+  // Let ProtoTraceTokenizer forward the packet to the parser.
+  return ModuleResult::Ignored();
+}
+
+void TrackEventTokenizer::TokenizeThreadDescriptor(
+    PacketSequenceState* state,
+    const protos::pbzero::ThreadDescriptor::Decoder&
+        thread_descriptor_decoder) {
+  // TODO(eseckler): Remove support for legacy thread descriptor-based default
+  // tracks and delta timestamps.
   state->SetThreadDescriptor(
       thread_descriptor_decoder.pid(), thread_descriptor_decoder.tid(),
       thread_descriptor_decoder.reference_timestamp_us() * 1000,
       thread_descriptor_decoder.reference_thread_time_us() * 1000,
       thread_descriptor_decoder.reference_thread_instruction_count());
-
-  TokenizeThreadDescriptor(thread_descriptor_decoder);
-}
-
-void TrackEventTokenizer::TokenizeThreadDescriptor(
-    const protos::pbzero::ThreadDescriptor::Decoder&
-        thread_descriptor_decoder) {
-  base::StringView name;
-  if (thread_descriptor_decoder.has_thread_name()) {
-    name = thread_descriptor_decoder.thread_name();
-  } else if (thread_descriptor_decoder.has_chrome_thread_type()) {
-    // TODO(skyostil): Remove parsing for legacy chrome_thread_type field.
-    using protos::pbzero::ThreadDescriptor;
-    switch (thread_descriptor_decoder.chrome_thread_type()) {
-      case ThreadDescriptor::CHROME_THREAD_MAIN:
-        name = "CrProcessMain";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_IO:
-        name = "ChromeIOThread";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_POOL_FG_WORKER:
-        name = "ThreadPoolForegroundWorker&";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_POOL_BG_WORKER:
-        name = "ThreadPoolBackgroundWorker&";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_POOL_FB_BLOCKING:
-        name = "ThreadPoolSingleThreadForegroundBlocking&";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_POOL_BG_BLOCKING:
-        name = "ThreadPoolSingleThreadBackgroundBlocking&";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_POOL_SERVICE:
-        name = "ThreadPoolService";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_COMPOSITOR_WORKER:
-        name = "CompositorTileWorker&";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_COMPOSITOR:
-        name = "Compositor";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_VIZ_COMPOSITOR:
-        name = "VizCompositorThread";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_SERVICE_WORKER:
-        name = "ServiceWorkerThread&";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_MEMORY_INFRA:
-        name = "MemoryInfra";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_SAMPLING_PROFILER:
-        name = "StackSamplingProfiler";
-        break;
-      case ThreadDescriptor::CHROME_THREAD_UNSPECIFIED:
-        name = "ChromeUnspecified";
-        break;
-    }
-  }
-
-  if (!name.empty()) {
-    auto thread_name_id = context_->storage->InternString(name);
-    ProcessTracker* procs = context_->process_tracker.get();
-    // Don't override system-provided names.
-    procs->SetThreadNameIfUnset(
-        procs->UpdateThread(
-            static_cast<uint32_t>(thread_descriptor_decoder.tid()),
-            static_cast<uint32_t>(thread_descriptor_decoder.pid())),
-        thread_name_id);
-  }
-}
-
-void TrackEventTokenizer::TokenizeChromeThreadDescriptor(
-    int32_t pid,
-    int32_t tid,
-    const protos::pbzero::ChromeThreadDescriptor::Decoder&
-        thread_descriptor_decoder) {
-  if (!thread_descriptor_decoder.has_thread_type())
-    return;
-
-  base::StringView name;
-  using protos::pbzero::ChromeThreadDescriptor;
-  // Thread names that end in an ampersand indicate multiple thread instances
-  // (e.g., CompositorTileWorker{1,2,...}) that have been collapsed into a
-  // single thread name.
-  switch (thread_descriptor_decoder.thread_type()) {
-    case ChromeThreadDescriptor::THREAD_MAIN:
-      name = "CrProcessMain";
-      break;
-    case ChromeThreadDescriptor::THREAD_IO:
-      name = "ChromeIOThread";
-      break;
-    case ChromeThreadDescriptor::THREAD_POOL_FG_WORKER:
-      name = "ThreadPoolForegroundWorker&";
-      break;
-    case ChromeThreadDescriptor::THREAD_POOL_BG_WORKER:
-      name = "ThreadPoolBackgroundWorker&";
-      break;
-    case ChromeThreadDescriptor::THREAD_POOL_FB_BLOCKING:
-      name = "ThreadPoolSingleThreadForegroundBlocking&";
-      break;
-    case ChromeThreadDescriptor::THREAD_POOL_BG_BLOCKING:
-      name = "ThreadPoolSingleThreadBackgroundBlocking&";
-      break;
-    case ChromeThreadDescriptor::THREAD_POOL_SERVICE:
-      name = "ThreadPoolService";
-      break;
-    case ChromeThreadDescriptor::THREAD_COMPOSITOR_WORKER:
-      name = "CompositorTileWorker&";
-      break;
-    case ChromeThreadDescriptor::THREAD_COMPOSITOR:
-      name = "Compositor";
-      break;
-    case ChromeThreadDescriptor::THREAD_VIZ_COMPOSITOR:
-      name = "VizCompositorThread";
-      break;
-    case ChromeThreadDescriptor::THREAD_SERVICE_WORKER:
-      name = "ServiceWorkerThread&";
-      break;
-    case ChromeThreadDescriptor::THREAD_MEMORY_INFRA:
-      name = "MemoryInfra";
-      break;
-    case ChromeThreadDescriptor::THREAD_SAMPLING_PROFILER:
-      name = "StackSamplingProfiler";
-      break;
-    case ChromeThreadDescriptor::THREAD_UNSPECIFIED:
-      name = "ChromeUnspecified";
-      break;
-  }
-
-  if (!name.empty()) {
-    auto thread_name_id = context_->storage->InternString(name);
-    ProcessTracker* procs = context_->process_tracker.get();
-    // Don't override system-provided names.
-    procs->SetThreadNameIfUnset(procs->UpdateThread(static_cast<uint32_t>(tid),
-                                                    static_cast<uint32_t>(pid)),
-                                thread_name_id);
-  }
 }
 
 void TrackEventTokenizer::TokenizeTrackEventPacket(
@@ -392,7 +219,7 @@
         protos::pbzero::ClockSnapshot::Clock::MONOTONIC, timestamp);
     if (trace_ts.has_value())
       timestamp = trace_ts.value();
-  } else if (packet_decoder.timestamp()) {
+  } else if (packet_decoder.has_timestamp()) {
     timestamp = packet_timestamp;
   } else {
     PERFETTO_ELOG("TrackEvent without valid timestamp");
diff --git a/src/trace_processor/importers/proto/track_event_tokenizer.h b/src/trace_processor/importers/proto/track_event_tokenizer.h
index 4c848f7..48fdf47 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.h
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
@@ -26,6 +27,7 @@
 namespace protos {
 namespace pbzero {
 class ChromeThreadDescriptor_Decoder;
+class ProcessDescriptor_Decoder;
 class ThreadDescriptor_Decoder;
 class TracePacket_Decoder;
 }  // namespace pbzero
@@ -41,28 +43,24 @@
  public:
   explicit TrackEventTokenizer(TraceProcessorContext* context);
 
-  void TokenizeTrackDescriptorPacket(
-      const protos::pbzero::TracePacket_Decoder&);
-  void TokenizeProcessDescriptorPacket(
-      const protos::pbzero::TracePacket_Decoder&);
-  void TokenizeThreadDescriptorPacket(
+  ModuleResult TokenizeTrackDescriptorPacket(
+      PacketSequenceState* state,
+      const protos::pbzero::TracePacket_Decoder&,
+      int64_t packet_timestamp);
+  ModuleResult TokenizeThreadDescriptorPacket(
       PacketSequenceState* state,
       const protos::pbzero::TracePacket_Decoder&);
-  void TokenizeThreadDescriptor(
-      const protos::pbzero::ThreadDescriptor_Decoder&);
-  void TokenizeChromeThreadDescriptor(
-      int32_t pid,
-      int32_t tid,
-      const protos::pbzero::ChromeThreadDescriptor_Decoder&);
   void TokenizeTrackEventPacket(PacketSequenceState* state,
                                 const protos::pbzero::TracePacket_Decoder&,
                                 TraceBlobView* packet,
                                 int64_t packet_timestamp);
 
  private:
-  TraceProcessorContext* context_;
+  void TokenizeThreadDescriptor(
+      PacketSequenceState* state,
+      const protos::pbzero::ThreadDescriptor_Decoder&);
 
-  std::array<StringId, 9> process_name_ids_;
+  TraceProcessorContext* context_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/systrace/systrace_parser.cc b/src/trace_processor/importers/systrace/systrace_parser.cc
index b6642a2..745a447 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser.cc
@@ -109,7 +109,8 @@
       StringId name_id = context_->storage->InternString(point.name);
       UniqueTid utid = context_->process_tracker->UpdateThread(pid, point.tgid);
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      context_->slice_tracker->Begin(ts, track_id, 0 /* cat */, name_id);
+      context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
+                                     name_id);
       break;
     }
 
@@ -142,7 +143,7 @@
       TrackId track_id = context_->track_tracker->InternAndroidAsyncTrack(
           name_id, upid, cookie);
       if (point.phase == 'S') {
-        context_->slice_tracker->Begin(ts, track_id, 0, name_id);
+        context_->slice_tracker->Begin(ts, track_id, kNullStringId, name_id);
       } else {
         context_->slice_tracker->End(ts, track_id);
       }
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index 2ffaac4..d23581c 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -27,6 +27,7 @@
 #include "src/trace_processor/track_tracker.h"
 
 #include <inttypes.h>
+#include <cctype>
 #include <string>
 #include <unordered_map>
 
@@ -37,9 +38,9 @@
 std::string SubstrTrim(const std::string& input) {
   std::string s = input;
   s.erase(s.begin(), std::find_if(s.begin(), s.end(),
-                                  [](int ch) { return !std::isspace(ch); }));
+                                  [](char ch) { return !std::isspace(ch); }));
   s.erase(std::find_if(s.rbegin(), s.rend(),
-                       [](int ch) { return !std::isspace(ch); })
+                       [](char ch) { return !std::isspace(ch); })
               .base(),
           s.end());
   return s;
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.h b/src/trace_processor/importers/systrace/systrace_trace_parser.h
index 1b5926b..26553e9 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.h
@@ -47,8 +47,8 @@
   util::Status ParseSingleSystraceEvent(const std::string& buffer);
 
   TraceProcessorContext* const context_;
-  const StringId sched_wakeup_name_id_ = 0;
-  const StringId cpu_idle_name_id_ = 0;
+  const StringId sched_wakeup_name_id_ = kNullStringId;
+  const StringId cpu_idle_name_id_ = kNullStringId;
   const std::regex line_matcher_;
 
   ParseState state_ = ParseState::kBeforeParse;
diff --git a/src/trace_processor/metadata.h b/src/trace_processor/metadata.h
index e7edcd8..2546cec 100644
--- a/src/trace_processor/metadata.h
+++ b/src/trace_processor/metadata.h
@@ -20,7 +20,7 @@
 #include <stddef.h>
 
 #include "src/trace_processor/containers/string_pool.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -45,7 +45,8 @@
   F(system_version,                    KeyType::kSingle,  Variadic::kString), \
   F(system_release,                    KeyType::kSingle,  Variadic::kString), \
   F(system_machine,                    KeyType::kSingle,  Variadic::kString), \
-  F(android_build_fingerprint,         KeyType::kSingle,  Variadic::kString)
+  F(android_build_fingerprint,         KeyType::kSingle,  Variadic::kString), \
+  F(trace_size_bytes,                  KeyType::kSingle,  Variadic::kInt)
 // clang-format on
 
 // Compile time list of metadata items.
@@ -55,6 +56,9 @@
   F(kMulti,  "multi")
 // clang-format
 
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
 #define PERFETTO_TP_META_TYPE_ENUM(varname, ...) varname
 enum class KeyType : size_t {
   PERFETTO_TP_METADATA_KEY_TYPES(PERFETTO_TP_META_TYPE_ENUM),
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 537f55e..92512a9 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -32,6 +32,7 @@
   "android/android_startup_cpu.sql",
   "android/android_package_list.sql",
   "android/heap_profile_callsites.sql",
+  "android/android_hwui_metric.sql",
   "android/java_heap_stats.sql",
   "android/process_unagg_mem_view.sql",
   "android/process_mem.sql",
@@ -56,46 +57,45 @@
            rebase_path(generated_header, root_build_dir),
          ]
   inputs = sql_files
-  outputs = [
-    generated_header,
-  ]
+  outputs = [ generated_header ]
   public_configs = [ ":gen_config" ]
 }
 
-source_set("lib") {
-  sources = [
-    "metrics.cc",
-    "metrics.descriptor.h",
-    "metrics.h",
-  ]
-  deps = [
-    "../../../gn:default_deps",
-    "../../../gn:sqlite",
-    "../../../include/perfetto/trace_processor",
-    "../../../protos/perfetto/common:zero",
-    "../../../protos/perfetto/metrics:zero",
-    "../../../protos/perfetto/metrics/android:zero",
-    "../../../protos/perfetto/trace_processor:metrics_impl_zero",
-    "../../base",
-    "../../protozero:protozero",
-    "../sqlite",
-  ]
-  public_deps = [
-    ":gen_merged_sql_metrics",
-    "../../trace_processor:descriptors",
-  ]
-}
+if (enable_perfetto_trace_processor_sqlite) {
+  source_set("lib") {
+    sources = [
+      "custom_options.descriptor.h",
+      "metrics.cc",
+      "metrics.descriptor.h",
+      "metrics.h",
+    ]
+    deps = [
+      "../../../gn:default_deps",
+      "../../../gn:sqlite",
+      "../../../include/perfetto/trace_processor",
+      "../../../protos/perfetto/common:zero",
+      "../../../protos/perfetto/metrics:zero",
+      "../../../protos/perfetto/metrics/android:zero",
+      "../../../protos/perfetto/trace_processor:metrics_impl_zero",
+      "../../base",
+      "../../protozero:protozero",
+      "../sqlite",
+    ]
+    public_deps = [
+      ":gen_merged_sql_metrics",
+      "../../trace_processor:descriptors",
+    ]
+  }
 
-perfetto_unittest_source_set("unittests") {
-  testonly = true
-  sources = [
-    "metrics_unittest.cc",
-  ]
-  deps = [
-    ":lib",
-    "../../../gn:default_deps",
-    "../../../gn:gtest_and_gmock",
-    "../../../gn:sqlite",
-    "../../../protos/perfetto/common:zero",
-  ]
+  perfetto_unittest_source_set("unittests") {
+    testonly = true
+    sources = [ "metrics_unittest.cc" ]
+    deps = [
+      ":lib",
+      "../../../gn:default_deps",
+      "../../../gn:gtest_and_gmock",
+      "../../../gn:sqlite",
+      "../../../protos/perfetto/common:zero",
+    ]
+  }
 }
diff --git a/src/trace_processor/metrics/android/android_hwui_metric.sql b/src/trace_processor/metrics/android/android_hwui_metric.sql
new file mode 100644
index 0000000..f7ba671
--- /dev/null
+++ b/src/trace_processor/metrics/android/android_hwui_metric.sql
@@ -0,0 +1,247 @@
+--
+-- Copyright 2020 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
+--
+--     https://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.
+--
+
+
+-- TOP processes that have a RenderThread, sorted by CPU time on RT
+CREATE VIEW hwui_processes AS
+SELECT
+  process.name as process_name,
+  process.upid as process_upid,
+  CAST(SUM(sched.dur) / 1e6 as INT64) as rt_cpu_time_ms,
+  thread.utid as render_thread_id
+FROM sched
+INNER JOIN thread ON (thread.utid = sched.utid AND thread.name='RenderThread')
+INNER JOIN process ON (process.upid = thread.upid)
+GROUP BY process.name
+ORDER BY rt_cpu_time_ms DESC;
+
+CREATE VIEW hwui_draw_frame AS
+SELECT
+  count(*) as draw_frame_count,
+  max(dur) as draw_frame_max,
+  min(dur) as draw_frame_min,
+  avg(dur) as draw_frame_avg,
+  thread_track.utid as render_thread_id
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name='DrawFrame' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_flush_commands AS
+SELECT
+  count(*) as flush_count,
+  max(dur) as flush_max,
+  min(dur) as flush_min,
+  avg(dur) as flush_avg,
+  thread_track.utid as render_thread_id
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name='flush commands' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_prepare_tree AS
+SELECT
+  count(*) as prepare_tree_count,
+  max(dur) as prepare_tree_max,
+  min(dur) as prepare_tree_min,
+  avg(dur) as prepare_tree_avg,
+  thread_track.utid as render_thread_id
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name='prepareTree' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_gpu_completion AS
+SELECT
+  count(*) as gpu_completion_count,
+  max(dur) as gpu_completion_max,
+  min(dur) as gpu_completion_min,
+  avg(dur) as gpu_completion_avg,
+  thread.upid as process_upid
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+INNER JOIN thread ON (thread.name='GPU completion' AND thread.utid = thread_track.utid)
+WHERE slice.name LIKE 'waiting for GPU completion%' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_ui_record AS
+SELECT
+  count(*) as ui_record_count,
+  max(dur) as ui_record_max,
+  min(dur) as ui_record_min,
+  avg(dur) as ui_record_avg,
+  thread.upid as process_upid
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+INNER JOIN thread ON (thread.name=substr(process.name,-15) AND thread.utid = thread_track.utid)
+INNER JOIN process ON (process.upid = thread.upid)
+WHERE slice.name='Record View#draw()' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_shader_compile AS
+SELECT
+  count(*) as shader_compile_count,
+  sum(dur) as shader_compile_time,
+  avg(dur) as shader_compile_avg,
+  thread_track.utid as render_thread_id
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name='shader_compile' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_cache_hit AS
+SELECT
+  count(*) as cache_hit_count,
+  sum(dur) as cache_hit_time,
+  avg(dur) as cache_hit_avg,
+  thread_track.utid as render_thread_id
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name='cache_hit' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_cache_miss AS
+SELECT
+  count(*) as cache_miss_count,
+  sum(dur) as cache_miss_time,
+  avg(dur) as cache_miss_avg,
+  thread_track.utid as render_thread_id
+FROM slice
+INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name='cache_miss' AND slice.dur >= 0
+GROUP BY thread_track.utid;
+
+CREATE VIEW hwui_graphics_cpu_mem AS
+SELECT
+  max(value) as graphics_cpu_mem_max,
+  min(value) as graphics_cpu_mem_min,
+  avg(value) as graphics_cpu_mem_avg,
+  process_counter_track.upid as process_upid
+FROM counter
+INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name='HWUI CPU Memory' AND counter.value >= 0
+GROUP BY process_counter_track.upid;
+
+CREATE VIEW hwui_graphics_gpu_mem AS
+SELECT
+  max(value) as graphics_gpu_mem_max,
+  min(value) as graphics_gpu_mem_min,
+  avg(value) as graphics_gpu_mem_avg,
+  process_counter_track.upid as process_upid
+FROM counter
+INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name='HWUI GPU Memory' AND counter.value >= 0
+GROUP BY process_counter_track.upid;
+
+CREATE VIEW hwui_texture_mem AS
+SELECT
+  max(value) as texture_mem_max,
+  min(value) as texture_mem_min,
+  avg(value) as texture_mem_avg,
+  process_counter_track.upid as process_upid
+FROM counter
+INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name='HWUI Texture Memory' AND counter.value >= 0
+GROUP BY process_counter_track.upid;
+
+CREATE VIEW hwui_all_mem AS
+SELECT
+  max(value) as all_mem_max,
+  min(value) as all_mem_min,
+  avg(value) as all_mem_avg,
+  process_counter_track.upid as process_upid
+FROM counter
+INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name='HWUI All Memory' AND counter.value >= 0
+GROUP BY process_counter_track.upid;
+
+CREATE VIEW android_hwui_metric_output AS
+SELECT AndroidHwuiMetric(
+  'process_info', (
+    SELECT RepeatedField(
+      ProcessRenderInfo(
+        'process_name', process_name,
+        'rt_cpu_time_ms', rt_cpu_time_ms,
+
+        'draw_frame_count', hwui_draw_frame.draw_frame_count,
+        'draw_frame_max', hwui_draw_frame.draw_frame_max,
+        'draw_frame_min', hwui_draw_frame.draw_frame_min,
+        'draw_frame_avg', hwui_draw_frame.draw_frame_avg,
+
+        'flush_count', hwui_flush_commands.flush_count,
+        'flush_max', hwui_flush_commands.flush_max,
+        'flush_min', hwui_flush_commands.flush_min,
+        'flush_avg', hwui_flush_commands.flush_avg,
+
+        'prepare_tree_count', hwui_prepare_tree.prepare_tree_count,
+        'prepare_tree_max', hwui_prepare_tree.prepare_tree_max,
+        'prepare_tree_min', hwui_prepare_tree.prepare_tree_min,
+        'prepare_tree_avg', hwui_prepare_tree.prepare_tree_avg,
+
+        'gpu_completion_count', hwui_gpu_completion.gpu_completion_count,
+        'gpu_completion_max', hwui_gpu_completion.gpu_completion_max,
+        'gpu_completion_min', hwui_gpu_completion.gpu_completion_min,
+        'gpu_completion_avg', hwui_gpu_completion.gpu_completion_avg,
+
+        'ui_record_count', hwui_ui_record.ui_record_count,
+        'ui_record_max', hwui_ui_record.ui_record_max,
+        'ui_record_min', hwui_ui_record.ui_record_min,
+        'ui_record_avg', hwui_ui_record.ui_record_avg,
+
+        'shader_compile_count', hwui_shader_compile.shader_compile_count,
+        'shader_compile_time', hwui_shader_compile.shader_compile_time,
+        'shader_compile_avg', hwui_shader_compile.shader_compile_avg,
+
+        'cache_hit_count', hwui_cache_hit.cache_hit_count,
+        'cache_hit_time', hwui_cache_hit.cache_hit_time,
+        'cache_hit_avg', hwui_cache_hit.cache_hit_avg,
+
+        'cache_miss_count', hwui_cache_miss.cache_miss_count,
+        'cache_miss_time', hwui_cache_miss.cache_miss_time,
+        'cache_miss_avg', hwui_cache_miss.cache_miss_avg,
+
+        'graphics_cpu_mem_max', CAST(hwui_graphics_cpu_mem.graphics_cpu_mem_max as INT64),
+        'graphics_cpu_mem_min', CAST(hwui_graphics_cpu_mem.graphics_cpu_mem_min as INT64),
+        'graphics_cpu_mem_avg', hwui_graphics_cpu_mem.graphics_cpu_mem_avg,
+
+        'graphics_gpu_mem_max', CAST(hwui_graphics_gpu_mem.graphics_gpu_mem_max as INT64),
+        'graphics_gpu_mem_min', CAST(hwui_graphics_gpu_mem.graphics_gpu_mem_min as INT64),
+        'graphics_gpu_mem_avg', hwui_graphics_gpu_mem.graphics_gpu_mem_avg,
+
+        'texture_mem_max', CAST(hwui_texture_mem.texture_mem_max as INT64),
+        'texture_mem_min', CAST(hwui_texture_mem.texture_mem_min as INT64),
+        'texture_mem_avg', hwui_texture_mem.texture_mem_avg,
+
+        'all_mem_max', CAST(hwui_all_mem.all_mem_max as INT64),
+        'all_mem_min', CAST(hwui_all_mem.all_mem_min as INT64),
+        'all_mem_avg', hwui_all_mem.all_mem_avg
+      )
+    )
+    FROM hwui_processes
+    LEFT JOIN hwui_draw_frame ON (hwui_draw_frame.render_thread_id = hwui_processes.render_thread_id)
+    LEFT JOIN hwui_flush_commands ON (hwui_flush_commands.render_thread_id = hwui_processes.render_thread_id)
+    LEFT JOIN hwui_prepare_tree ON (hwui_prepare_tree.render_thread_id = hwui_processes.render_thread_id)
+    LEFT JOIN hwui_gpu_completion ON (hwui_gpu_completion.process_upid = hwui_processes.process_upid)
+    LEFT JOIN hwui_ui_record ON (hwui_ui_record.process_upid = hwui_processes.process_upid)
+    LEFT JOIN hwui_shader_compile ON (hwui_shader_compile.render_thread_id = hwui_processes.render_thread_id)
+    LEFT JOIN hwui_cache_hit ON (hwui_cache_hit.render_thread_id = hwui_processes.render_thread_id)
+    LEFT JOIN hwui_cache_miss ON (hwui_cache_miss.render_thread_id = hwui_processes.render_thread_id)
+    LEFT JOIN hwui_graphics_cpu_mem ON (hwui_graphics_cpu_mem.process_upid = hwui_processes.process_upid)
+    LEFT JOIN hwui_graphics_gpu_mem ON (hwui_graphics_gpu_mem.process_upid = hwui_processes.process_upid)
+    LEFT JOIN hwui_texture_mem ON (hwui_texture_mem.process_upid = hwui_processes.process_upid)
+    LEFT JOIN hwui_all_mem ON (hwui_all_mem.process_upid = hwui_processes.process_upid)
+  )
+);
diff --git a/src/trace_processor/metrics/android/android_startup_launches.sql b/src/trace_processor/metrics/android/android_startup_launches.sql
index 63b38ac..9b8caf1 100644
--- a/src/trace_processor/metrics/android/android_startup_launches.sql
+++ b/src/trace_processor/metrics/android/android_startup_launches.sql
@@ -32,7 +32,7 @@
 SELECT
   ts,
   package_name,
-  type
+  launching_events_helper.type
 FROM raw
 CROSS JOIN launching_events_helper
 JOIN thread USING(utid)
diff --git a/src/trace_processor/metrics/android/unmapped_java_symbols.sql b/src/trace_processor/metrics/android/unmapped_java_symbols.sql
index c2e7785..e25745c 100644
--- a/src/trace_processor/metrics/android/unmapped_java_symbols.sql
+++ b/src/trace_processor/metrics/android/unmapped_java_symbols.sql
@@ -19,7 +19,22 @@
 CREATE TABLE IF NOT EXISTS types_per_upid AS
 WITH distinct_unmapped_type_names AS (
   SELECT DISTINCT upid, type_name
-  FROM heap_graph_object WHERE deobfuscated_type_name IS NULL
+  FROM (
+    SELECT upid, RTRIM(type_name, '[]') AS type_name
+    FROM heap_graph_object
+    WHERE deobfuscated_type_name IS NULL
+  )
+  WHERE type_name NOT IN ('byte', 'char', 'short', 'int', 'long', 'boolean', 'float', 'double')
+  AND type_name NOT LIKE '$Proxy%'
+  AND type_name NOT LIKE 'java.%'
+  AND type_name NOT LIKE 'javax.%'
+  AND type_name NOT LIKE 'j$.%'
+  AND type_name NOT LIKE 'android.%'
+  AND type_name NOT LIKE 'com.android.%'
+  AND type_name NOT LIKE 'sun.%'
+  AND type_name NOT LIKE 'dalvik.%'
+  AND type_name NOT LIKE 'libcore.%'
+  AND LENGTH(type_name) > 0
 )
 SELECT upid, RepeatedField(type_name) AS types
 FROM distinct_unmapped_type_names GROUP BY 1;
@@ -29,6 +44,16 @@
   SELECT DISTINCT upid, field_name
   FROM heap_graph_object JOIN heap_graph_reference USING (reference_set_id)
   WHERE deobfuscated_type_name IS NULL
+  AND field_name NOT LIKE '$Proxy%'
+  AND field_name NOT LIKE 'java.%'
+  AND field_name NOT LIKE 'javax.%'
+  AND field_name NOT LIKE 'j$.%'
+  AND field_name NOT LIKE 'android.%'
+  AND field_name NOT LIKE 'com.android.%'
+  AND field_name NOT LIKE 'sun.%'
+  AND field_name NOT LIKE 'dalvik.%'
+  AND field_name NOT LIKE 'libcore.%'
+  AND LENGTH(field_name) > 0
 )
 SELECT upid, RepeatedField(field_name) AS fields
 FROM distinct_unmapped_field_names GROUP BY 1;
diff --git a/src/trace_processor/metrics/custom_options.descriptor.h b/src/trace_processor/metrics/custom_options.descriptor.h
new file mode 100644
index 0000000..3829d74
--- /dev/null
+++ b/src/trace_processor/metrics/custom_options.descriptor.h
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2019 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_TRACE_PROCESSOR_METRICS_CUSTOM_OPTIONS_DESCRIPTOR_H_
+#define SRC_TRACE_PROCESSOR_METRICS_CUSTOM_OPTIONS_DESCRIPTOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <array>
+
+// This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
+
+// SHA1(tools/gen_binary_descriptors)
+// d6628b15181dba5287e35b56b966b39ea93d42b1
+// SHA1(protos/perfetto/metrics/custom_options.proto)
+// 074c971d85f72345988fb3279345cd6b3dabde9c
+
+// This is the proto CustomOptions encoded as a ProtoFileDescriptor to allow
+// for reflection without libprotobuf full/non-lite protos.
+
+namespace perfetto {
+
+constexpr std::array<uint8_t, 7803> kCustomOptionsDescriptor{
+    {0x0a, 0x9b, 0x3b, 0x0a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73,
+     0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x4d, 0x0a, 0x11, 0x46, 0x69,
+     0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
+     0x53, 0x65, 0x74, 0x12, 0x38, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18,
+     0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x04, 0x66, 0x69, 0x6c,
+     0x65, 0x22, 0xe4, 0x04, 0x0a, 0x13, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a,
+     0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12,
+     0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63,
+     0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70,
+     0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x70,
+     0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64,
+     0x65, 0x6e, 0x63, 0x79, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x05, 0x52, 0x10,
+     0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64,
+     0x65, 0x6e, 0x63, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x65, 0x61, 0x6b,
+     0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18,
+     0x0b, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0e, 0x77, 0x65, 0x61, 0x6b, 0x44,
+     0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x43, 0x0a,
+     0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70,
+     0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f,
+     0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+     0x66, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
+     0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61,
+     0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x65, 0x6e,
+     0x75, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28,
+     0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d,
+     0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72,
+     0x6f, 0x74, 0x6f, 0x52, 0x08, 0x65, 0x6e, 0x75, 0x6d, 0x54, 0x79, 0x70,
+     0x65, 0x12, 0x41, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+     0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x6f,
+     0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+     0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63,
+     0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52,
+     0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x43, 0x0a, 0x09,
+     0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69,
+     0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+     0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65,
+     0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x6f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
+     0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+     0x73, 0x12, 0x49, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
+     0x63, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x09, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x6f,
+     0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f,
+     0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65,
+     0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6e, 0x74,
+     0x61, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79,
+     0x6e, 0x74, 0x61, 0x78, 0x22, 0xb9, 0x06, 0x0a, 0x0f, 0x44, 0x65, 0x73,
+     0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f,
+     0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x05,
+     0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
+     0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44,
+     0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f,
+     0x74, 0x6f, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x43, 0x0a,
+     0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+     0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46,
+     0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x09, 0x65, 0x78, 0x74,
+     0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0b, 0x6e, 0x65,
+     0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x52, 0x0a, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70,
+     0x65, 0x12, 0x41, 0x0a, 0x09, 0x65, 0x6e, 0x75, 0x6d, 0x5f, 0x74, 0x79,
+     0x70, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67,
+     0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+     0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72,
+     0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x08,
+     0x65, 0x6e, 0x75, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x58, 0x0a, 0x0f,
+     0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x61,
+     0x6e, 0x67, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e,
+     0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x62, 0x75, 0x66, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65,
+     0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e,
+     0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e,
+     0x67, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x5f,
+     0x64, 0x65, 0x63, 0x6c, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25,
+     0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x52, 0x09, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65, 0x63, 0x6c,
+     0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+     0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+     0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+     0x55, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f,
+     0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32,
+     0x2e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
+     0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65,
+     0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52,
+     0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e,
+     0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76,
+     0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28,
+     0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x4e,
+     0x61, 0x6d, 0x65, 0x1a, 0x7a, 0x0a, 0x0e, 0x45, 0x78, 0x74, 0x65, 0x6e,
+     0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a,
+     0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
+     0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65,
+     0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e,
+     0x64, 0x12, 0x40, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x6f,
+     0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+     0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61,
+     0x6e, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07,
+     0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x37, 0x0a, 0x0d, 0x52,
+     0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65,
+     0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10,
+     0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
+     0x03, 0x65, 0x6e, 0x64, 0x22, 0x7c, 0x0a, 0x15, 0x45, 0x78, 0x74, 0x65,
+     0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b,
+     0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74,
+     0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69,
+     0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70,
+     0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a,
+     0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0x98,
+     0x06, 0x0a, 0x14, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63,
+     0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+     0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e,
+     0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
+     0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x05, 0x6c,
+     0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b,
+     0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x61, 0x62,
+     0x65, 0x6c, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05,
+     0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+     0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46,
+     0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x79, 0x70, 0x65,
+     0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79,
+     0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12,
+     0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x65, 0x18,
+     0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e,
+     0x64, 0x65, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75,
+     0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56,
+     0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x6e, 0x65, 0x6f,
+     0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x0a, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x49, 0x6e, 0x64, 0x65,
+     0x78, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x73,
+     0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69,
+     0x6f, 0x6e, 0x73, 0x22, 0xb6, 0x02, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65,
+     0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x55,
+     0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50,
+     0x45, 0x5f, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a,
+     0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10,
+     0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49,
+     0x4e, 0x54, 0x36, 0x34, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59,
+     0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x05, 0x12, 0x10,
+     0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x58, 0x45, 0x44,
+     0x36, 0x34, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45,
+     0x5f, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, 0x10, 0x07, 0x12, 0x0d,
+     0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10,
+     0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54,
+     0x52, 0x49, 0x4e, 0x47, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59,
+     0x50, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x0a, 0x12, 0x10,
+     0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41,
+     0x47, 0x45, 0x10, 0x0b, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45,
+     0x5f, 0x42, 0x59, 0x54, 0x45, 0x53, 0x10, 0x0c, 0x12, 0x0f, 0x0a, 0x0b,
+     0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10,
+     0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e,
+     0x55, 0x4d, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45,
+     0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, 0x10, 0x0f, 0x12,
+     0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58,
+     0x45, 0x44, 0x36, 0x34, 0x10, 0x10, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59,
+     0x50, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x11, 0x12,
+     0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x54,
+     0x36, 0x34, 0x10, 0x12, 0x22, 0x43, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65,
+     0x6c, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x4f,
+     0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x12, 0x0a,
+     0x0e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49,
+     0x52, 0x45, 0x44, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42,
+     0x45, 0x4c, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10,
+     0x03, 0x22, 0x63, 0x0a, 0x14, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a,
+     0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65,
+     0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x13, 0x45,
+     0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+     0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+     0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+     0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x73,
+     0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f,
+     0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x6f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69,
+     0x6f, 0x6e, 0x73, 0x12, 0x5d, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72,
+     0x76, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e,
+     0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
+     0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x65,
+     0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52,
+     0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e,
+     0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76,
+     0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28,
+     0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x4e,
+     0x61, 0x6d, 0x65, 0x1a, 0x3b, 0x0a, 0x11, 0x45, 0x6e, 0x75, 0x6d, 0x52,
+     0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65,
+     0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10,
+     0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
+     0x03, 0x65, 0x6e, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x75,
+     0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
+     0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d,
+     0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e,
+     0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21,
+     0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c,
+     0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa7, 0x01, 0x0a, 0x16, 0x53,
+     0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
+     0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x06, 0x6d, 0x65, 0x74,
+     0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e,
+     0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x39, 0x0a,
+     0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72,
+     0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
+     0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x89, 0x02, 0x0a,
+     0x15, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72,
+     0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12,
+     0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+     0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e,
+     0x70, 0x75, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70,
+     0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f,
+     0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
+     0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x38,
+     0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65,
+     0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
+     0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x10,
+     0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61,
+     0x6d, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05,
+     0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e,
+     0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x30,
+     0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72,
+     0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08,
+     0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0f, 0x73, 0x65, 0x72,
+     0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67,
+     0x22, 0x92, 0x09, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6a, 0x61, 0x76, 0x61,
+     0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x0b, 0x6a, 0x61, 0x76, 0x61, 0x50, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6a, 0x61, 0x76, 0x61, 0x5f,
+     0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e,
+     0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6a,
+     0x61, 0x76, 0x61, 0x4f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73,
+     0x73, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x6a, 0x61, 0x76,
+     0x61, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x66,
+     0x69, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05,
+     0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x11, 0x6a, 0x61, 0x76, 0x61, 0x4d,
+     0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73,
+     0x12, 0x44, 0x0a, 0x1d, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x67, 0x65, 0x6e,
+     0x65, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73,
+     0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x14, 0x20,
+     0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x19, 0x6a, 0x61, 0x76,
+     0x61, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, 0x71, 0x75,
+     0x61, 0x6c, 0x73, 0x41, 0x6e, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a,
+     0x0a, 0x16, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e,
+     0x67, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x74, 0x66, 0x38,
+     0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73,
+     0x65, 0x52, 0x13, 0x6a, 0x61, 0x76, 0x61, 0x53, 0x74, 0x72, 0x69, 0x6e,
+     0x67, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x74, 0x66, 0x38, 0x12, 0x53,
+     0x0a, 0x0c, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x5f, 0x66,
+     0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x67,
+     0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+     0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+     0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x4d,
+     0x6f, 0x64, 0x65, 0x3a, 0x05, 0x53, 0x50, 0x45, 0x45, 0x44, 0x52, 0x0b,
+     0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x46, 0x6f, 0x72, 0x12,
+     0x1d, 0x0a, 0x0a, 0x67, 0x6f, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+     0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x6f, 0x50,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x63, 0x63,
+     0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72,
+     0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x3a,
+     0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x11, 0x63, 0x63, 0x47, 0x65,
+     0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+     0x73, 0x12, 0x39, 0x0a, 0x15, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x67, 0x65,
+     0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+     0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61,
+     0x6c, 0x73, 0x65, 0x52, 0x13, 0x6a, 0x61, 0x76, 0x61, 0x47, 0x65, 0x6e,
+     0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+     0x12, 0x35, 0x0a, 0x13, 0x70, 0x79, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72,
+     0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18,
+     0x12, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65,
+     0x52, 0x11, 0x70, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53,
+     0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x14, 0x70,
+     0x68, 0x70, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73,
+     0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x2a, 0x20, 0x01, 0x28,
+     0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x12, 0x70, 0x68,
+     0x70, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76,
+     0x69, 0x63, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72,
+     0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08,
+     0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70,
+     0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x10, 0x63,
+     0x63, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x72, 0x65,
+     0x6e, 0x61, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66,
+     0x61, 0x6c, 0x73, 0x65, 0x52, 0x0e, 0x63, 0x63, 0x45, 0x6e, 0x61, 0x62,
+     0x6c, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73, 0x12, 0x2a, 0x0a, 0x11,
+     0x6f, 0x62, 0x6a, 0x63, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x70,
+     0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, 0x52,
+     0x0f, 0x6f, 0x62, 0x6a, 0x63, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x50, 0x72,
+     0x65, 0x66, 0x69, 0x78, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x73, 0x68, 0x61,
+     0x72, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
+     0x18, 0x25, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x73, 0x68, 0x61,
+     0x72, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12,
+     0x21, 0x0a, 0x0c, 0x73, 0x77, 0x69, 0x66, 0x74, 0x5f, 0x70, 0x72, 0x65,
+     0x66, 0x69, 0x78, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73,
+     0x77, 0x69, 0x66, 0x74, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x28,
+     0x0a, 0x10, 0x70, 0x68, 0x70, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f,
+     0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09,
+     0x52, 0x0e, 0x70, 0x68, 0x70, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x50, 0x72,
+     0x65, 0x66, 0x69, 0x78, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x68, 0x70, 0x5f,
+     0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x29, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x68, 0x70, 0x4e, 0x61, 0x6d, 0x65,
+     0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x68, 0x70,
+     0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x61,
+     0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x14, 0x70, 0x68, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+     0x74, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12,
+     0x21, 0x0a, 0x0c, 0x72, 0x75, 0x62, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72,
+     0x75, 0x62, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x58,
+     0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65,
+     0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7,
+     0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65,
+     0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x0a, 0x0c, 0x4f, 0x70, 0x74, 0x69,
+     0x6d, 0x69, 0x7a, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x09, 0x0a, 0x05,
+     0x53, 0x50, 0x45, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43,
+     0x4f, 0x44, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x10, 0x02, 0x12, 0x10,
+     0x0a, 0x0c, 0x4c, 0x49, 0x54, 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49,
+     0x4d, 0x45, 0x10, 0x03, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80,
+     0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x22, 0xd1, 0x02,
+     0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x73,
+     0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x72, 0x65,
+     0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x14, 0x6d, 0x65,
+     0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x57, 0x69, 0x72, 0x65,
+     0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x4c, 0x0a, 0x1f, 0x6e, 0x6f,
+     0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x63,
+     0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x3a,
+     0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x1c, 0x6e, 0x6f, 0x53, 0x74,
+     0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
+     0x70, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72,
+     0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74,
+     0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61,
+     0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61,
+     0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x70, 0x5f, 0x65,
+     0x6e, 0x74, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
+     0x6d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x58, 0x0a, 0x14,
+     0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65,
+     0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e,
+     0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74,
+     0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69,
+     0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80,
+     0x02, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10,
+     0x0a, 0x22, 0xe2, 0x03, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x63, 0x74,
+     0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e,
+     0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x06,
+     0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x52, 0x05, 0x63, 0x74, 0x79, 0x70,
+     0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18,
+     0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65,
+     0x64, 0x12, 0x47, 0x0a, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x18,
+     0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+     0x2e, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x09, 0x4a, 0x53, 0x5f,
+     0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x52, 0x06, 0x6a, 0x73, 0x74, 0x79,
+     0x70, 0x65, 0x12, 0x19, 0x0a, 0x04, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x05,
+     0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52,
+     0x04, 0x6c, 0x61, 0x7a, 0x79, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70,
+     0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65,
+     0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x04,
+     0x77, 0x65, 0x61, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05,
+     0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x12,
+     0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72,
+     0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+     0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f,
+     0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+     0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74,
+     0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e,
+     0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2f, 0x0a, 0x05, 0x43, 0x54, 0x79,
+     0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47,
+     0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x4f, 0x52, 0x44, 0x10, 0x01,
+     0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x50,
+     0x49, 0x45, 0x43, 0x45, 0x10, 0x02, 0x22, 0x35, 0x0a, 0x06, 0x4a, 0x53,
+     0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e,
+     0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4a,
+     0x53, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0d,
+     0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10,
+     0x02, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02,
+     0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x73, 0x0a, 0x0c, 0x4f, 0x6e,
+     0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x58,
+     0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65,
+     0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7,
+     0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65,
+     0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80,
+     0x80, 0x80, 0x02, 0x22, 0xc0, 0x01, 0x0a, 0x0b, 0x45, 0x6e, 0x75, 0x6d,
+     0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61,
+     0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41,
+     0x6c, 0x69, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72,
+     0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
+     0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70,
+     0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x58, 0x0a, 0x14, 0x75,
+     0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64,
+     0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03,
+     0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65,
+     0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+     0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02,
+     0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x9e, 0x01, 0x0a, 0x10, 0x45,
+     0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69,
+     0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65,
+     0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x3a,
+     0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72,
+     0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e,
+     0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f,
+     0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28,
+     0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e,
+     0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72,
+     0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+     0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22,
+     0x9c, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65,
+     0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01,
+     0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64,
+     0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x58, 0x0a,
+     0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74,
+     0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+     0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55,
+     0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64,
+     0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e,
+     0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80,
+     0x80, 0x02, 0x22, 0xe0, 0x02, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x68, 0x6f,
+     0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a,
+     0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x21,
+     0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52,
+     0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12,
+     0x71, 0x0a, 0x11, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e,
+     0x63, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x22, 0x20, 0x01,
+     0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74,
+     0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49,
+     0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65,
+     0x76, 0x65, 0x6c, 0x3a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54,
+     0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
+     0x52, 0x10, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63,
+     0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e,
+     0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f,
+     0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28,
+     0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e,
+     0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72,
+     0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+     0x22, 0x50, 0x0a, 0x10, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65,
+     0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x17, 0x0a, 0x13,
+     0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f,
+     0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a,
+     0x0f, 0x4e, 0x4f, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x45, 0x46, 0x46,
+     0x45, 0x43, 0x54, 0x53, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x44,
+     0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x2a, 0x09,
+     0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0x9a, 0x03,
+     0x0a, 0x13, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65,
+     0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
+     0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65,
+     0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+     0x6e, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x52, 0x04,
+     0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, 0x65, 0x6e,
+     0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x64, 0x65, 0x6e,
+     0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
+     0x2c, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f,
+     0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x04, 0x52, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76,
+     0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a,
+     0x12, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e,
+     0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x10, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49,
+     0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64,
+     0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+     0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+     0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74,
+     0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07,
+     0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+     0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67,
+     0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
+     0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72,
+     0x65, 0x67, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x4a,
+     0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1b,
+     0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x18,
+     0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x50,
+     0x61, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x65, 0x78,
+     0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x02, 0x28,
+     0x08, 0x52, 0x0b, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+     0x6f, 0x6e, 0x22, 0xa7, 0x02, 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63,
+     0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a,
+     0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x6f,
+     0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f,
+     0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c,
+     0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xce, 0x01, 0x0a, 0x08,
+     0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04,
+     0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02,
+     0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x04,
+     0x73, 0x70, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02,
+     0x10, 0x01, 0x52, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x12, 0x29, 0x0a, 0x10,
+     0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d,
+     0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f,
+     0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65,
+     0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x69, 0x6c,
+     0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+     0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x69,
+     0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+     0x12, 0x3a, 0x0a, 0x19, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f,
+     0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6d,
+     0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52,
+     0x17, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61,
+     0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+     0x22, 0xd1, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
+     0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4d,
+     0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+     0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f,
+     0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+     0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f,
+     0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x6d, 0x0a, 0x0a, 0x41, 0x6e, 0x6e,
+     0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70,
+     0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10,
+     0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+     0x46, 0x69, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x65, 0x67, 0x69,
+     0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x62, 0x65, 0x67,
+     0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x8f, 0x01, 0x0a,
+     0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x42, 0x10, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x48, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+     0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x64,
+     0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x3b, 0x64, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0xf8, 0x01, 0x01, 0xa2,
+     0x02, 0x03, 0x47, 0x50, 0x42, 0xaa, 0x02, 0x1a, 0x47, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0xda,
+     0x01, 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+     0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x33, 0x0a,
+     0x04, 0x75, 0x6e, 0x69, 0x74, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+     0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+     0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+     0x18, 0xd1, 0x86, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x6e,
+     0x69, 0x74, 0x3a, 0x53, 0x0a, 0x14, 0x69, 0x6d, 0x70, 0x72, 0x6f, 0x76,
+     0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
+     0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65,
+     0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd2, 0x86,
+     0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6d, 0x70, 0x72, 0x6f,
+     0x76, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74,
+     0x69, 0x6f, 0x6e}};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_METRICS_CUSTOM_OPTIONS_DESCRIPTOR_H_
diff --git a/src/trace_processor/metrics/metrics.descriptor.h b/src/trace_processor/metrics/metrics.descriptor.h
index 6e19075..f703d36 100644
--- a/src/trace_processor/metrics/metrics.descriptor.h
+++ b/src/trace_processor/metrics/metrics.descriptor.h
@@ -25,17 +25,17 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// f242f1ac484bbe7ba4c45e77b56ab588f8015196
+// d6628b15181dba5287e35b56b966b39ea93d42b1
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// 768c8fa5ed7f48eb644a1a45bb7295e32f8c771c
+// 29445577a551e8035ed8e99128ae38062fa77ee0
 
 // This is the proto Metrics encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 12018> kMetricsDescriptor{
-    {0x0a, 0x98, 0x03, 0x0a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+constexpr std::array<uint8_t, 14060> kMetricsDescriptor{
+    {0x0a, 0x94, 0x03, 0x0a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
      0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
      0x2f, 0x62, 0x61, 0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
@@ -68,75 +68,54 @@
      0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x55, 0x61, 0x12, 0x24, 0x0a,
      0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x67,
      0x5f, 0x75, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x63,
-     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x76, 0x67, 0x55, 0x61, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0xcb, 0x07, 0x0a, 0x30, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
-     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x22, 0x81, 0x07, 0x0a, 0x10, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x4c,
-     0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e,
-     0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70,
-     0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-     0x49, 0x6e, 0x66, 0x6f, 0x1a, 0xa8, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x63, 0x79, 0x63,
-     0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d,
-     0x63, 0x79, 0x63, 0x6c, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x75,
-     0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4e,
-     0x73, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x65,
-     0x71, 0x5f, 0x6b, 0x68, 0x7a, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x0a, 0x6d, 0x69, 0x6e, 0x46, 0x72, 0x65, 0x71, 0x4b, 0x68, 0x7a, 0x12,
-     0x20, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x5f,
-     0x6b, 0x68, 0x7a, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d,
-     0x61, 0x78, 0x46, 0x72, 0x65, 0x71, 0x4b, 0x68, 0x7a, 0x12, 0x20, 0x0a,
-     0x0c, 0x61, 0x76, 0x67, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x5f, 0x6b, 0x68,
-     0x7a, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x76, 0x67,
-     0x46, 0x72, 0x65, 0x71, 0x4b, 0x68, 0x7a, 0x1a, 0x65, 0x0a, 0x08, 0x43,
-     0x6f, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69,
-     0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12,
-     0x43, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x06,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x76, 0x67, 0x55, 0x61, 0x0a,
+     0xc7, 0x07, 0x0a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+     0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x81, 0x07,
+     0x0a, 0x10, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x4c, 0x0a, 0x0c, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
      0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41,
      0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52,
-     0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x4a, 0x04, 0x08, 0x02,
-     0x10, 0x06, 0x1a, 0x67, 0x0a, 0x0c, 0x43, 0x6f, 0x72, 0x65, 0x54, 0x79,
-     0x70, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
-     0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79,
-     0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75,
-     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a,
-     0xf4, 0x01, 0x0a, 0x06, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x12, 0x12,
-     0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x6d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x73, 0x12, 0x3e, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18,
-     0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x44, 0x61, 0x74,
-     0x61, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x4b, 0x0a, 0x09, 0x63,
-     0x6f, 0x72, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x03,
-     0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x44, 0x61,
-     0x74, 0x61, 0x52, 0x08, 0x63, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65,
-     0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0xac, 0x01, 0x0a, 0x07, 0x50,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+     0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52,
+     0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f,
+     0x1a, 0xa8, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x12, 0x18, 0x0a, 0x07, 0x6d, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x73, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x63, 0x79, 0x63, 0x6c,
+     0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d,
+     0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
+     0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x20, 0x0a,
+     0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x5f, 0x6b, 0x68,
+     0x7a, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e,
+     0x46, 0x72, 0x65, 0x71, 0x4b, 0x68, 0x7a, 0x12, 0x20, 0x0a, 0x0c, 0x6d,
+     0x61, 0x78, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x5f, 0x6b, 0x68, 0x7a, 0x18,
+     0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x46, 0x72,
+     0x65, 0x71, 0x4b, 0x68, 0x7a, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x76, 0x67,
+     0x5f, 0x66, 0x72, 0x65, 0x71, 0x5f, 0x6b, 0x68, 0x7a, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x76, 0x67, 0x46, 0x72, 0x65, 0x71,
+     0x4b, 0x68, 0x7a, 0x1a, 0x65, 0x0a, 0x08, 0x43, 0x6f, 0x72, 0x65, 0x44,
+     0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x07, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x06, 0x1a, 0x67,
+     0x0a, 0x0c, 0x43, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x44, 0x61,
+     0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x43,
+     0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0xf4, 0x01, 0x0a, 0x06,
+     0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
      0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
      0x6d, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
      0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65,
@@ -144,100 +123,120 @@
      0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75,
      0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69,
      0x63, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12,
-     0x42, 0x0a, 0x07, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x02,
-     0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x52, 0x07,
-     0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10,
-     0x04, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x93, 0x08, 0x0a, 0x30, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x22, 0xc9, 0x07, 0x0a, 0x13, 0x41, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x12, 0x5c, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01,
-     0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
-     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0e, 0x70,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x1a, 0xfd, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73,
-     0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0c,
-     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x61, 0x0a, 0x0e, 0x74,
-     0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d,
-     0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0d, 0x74, 0x6f, 0x74,
-     0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x65,
-     0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x62,
-     0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x03,
-     0x28, 0x0b, 0x32, 0x36, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74,
-     0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x11,
-     0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x72, 0x65, 0x61,
-     0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x1a, 0x87, 0x01, 0x0a, 0x11, 0x50, 0x72,
-     0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64,
-     0x6f, 0x77, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72,
-     0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
-     0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x56, 0x0a, 0x08, 0x63,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x3a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x3e, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28,
+     0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
      0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d,
-     0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x73, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x1a,
-     0x88, 0x03, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d,
-     0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x73, 0x12, 0x47, 0x0a, 0x08, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x72, 0x73,
-     0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d,
-     0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x43, 0x6f,
-     0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x61, 0x6e, 0x6f, 0x6e, 0x52,
-     0x73, 0x73, 0x12, 0x47, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x72,
-     0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70,
+     0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x43, 0x6f, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x63,
+     0x6f, 0x72, 0x65, 0x12, 0x4b, 0x0a, 0x09, 0x63, 0x6f, 0x72, 0x65, 0x5f,
+     0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x43, 0x6f,
+     0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08,
+     0x63, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x4a, 0x04, 0x08, 0x03,
+     0x10, 0x04, 0x1a, 0xac, 0x01, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x43,
+     0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x07,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x07, 0x74,
+     0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+     0x32, 0x28, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x52, 0x07, 0x74, 0x68, 0x72, 0x65,
+     0x61, 0x64, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x0a, 0x8f, 0x08,
+     0x0a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65,
+     0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xc9, 0x07, 0x0a, 0x13,
+     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72,
+     0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x5c, 0x0a, 0x0f, 0x70,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70,
      0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
      0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65,
-     0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x43,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65,
-     0x52, 0x73, 0x73, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x77, 0x61, 0x70, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x50,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x73, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0xfd, 0x01, 0x0a, 0x0e, 0x50, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f,
+     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12,
+     0x61, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x3a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x6d,
+     0x6f, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52,
+     0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x73, 0x12, 0x65, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69,
+     0x74, 0x79, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e,
+     0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f,
+     0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x69,
+     0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f,
+     0x77, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79,
+     0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x1a, 0x87, 0x01,
+     0x0a, 0x11, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x72,
+     0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70,
+     0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12,
+     0x56, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18,
+     0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
      0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72,
-     0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x43, 0x6f, 0x75, 0x6e,
-     0x74, 0x65, 0x72, 0x52, 0x04, 0x73, 0x77, 0x61, 0x70, 0x12, 0x50, 0x0a,
-     0x0d, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x73, 0x77,
-     0x61, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65,
-     0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x43,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x0b, 0x61, 0x6e, 0x6f, 0x6e,
-     0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x12, 0x49, 0x0a, 0x09, 0x6a,
-     0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x52, 0x08, 0x6a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x1a, 0x3f,
-     0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a,
-     0x03, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03,
-     0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x10, 0x0a,
-     0x03, 0x61, 0x76, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03,
-     0x61, 0x76, 0x67, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xa4, 0x06, 0x0a, 0x36,
+     0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+     0x65, 0x72, 0x73, 0x1a, 0x88, 0x03, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x08, 0x61, 0x6e, 0x6f,
+     0x6e, 0x5f, 0x72, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x61,
+     0x6e, 0x6f, 0x6e, 0x52, 0x73, 0x73, 0x12, 0x47, 0x0a, 0x08, 0x66, 0x69,
+     0x6c, 0x65, 0x5f, 0x72, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07,
+     0x66, 0x69, 0x6c, 0x65, 0x52, 0x73, 0x73, 0x12, 0x40, 0x0a, 0x04, 0x73,
+     0x77, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d,
+     0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x04, 0x73, 0x77, 0x61,
+     0x70, 0x12, 0x50, 0x0a, 0x0d, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x61, 0x6e,
+     0x64, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x0b,
+     0x61, 0x6e, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x12,
+     0x49, 0x0a, 0x09, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70,
+     0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f,
+     0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x52, 0x08, 0x6a, 0x61, 0x76, 0x61, 0x48, 0x65,
+     0x61, 0x70, 0x1a, 0x3f, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x01, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d,
+     0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x61,
+     0x78, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x76, 0x67, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x01, 0x52, 0x03, 0x61, 0x76, 0x67, 0x0a, 0xa0, 0x06, 0x0a, 0x36,
      0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
      0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
      0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f,
@@ -304,56 +303,55 @@
      0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08,
      0x6f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05,
      0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52,
-     0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xaf,
-     0x02, 0x0a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69,
-     0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xe5, 0x01, 0x0a,
-     0x10, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x4d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x40, 0x0a, 0x06, 0x62, 0x75, 0x66,
-     0x66, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49,
-     0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x42, 0x75, 0x66,
-     0x66, 0x65, 0x72, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x1a,
-     0x8e, 0x01, 0x0a, 0x06, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x12, 0x12,
-     0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x76,
-     0x67, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x61, 0x76, 0x67, 0x53,
-     0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e,
-     0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74,
-     0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6d, 0x69,
-     0x6e, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24,
-     0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62,
-     0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c,
-     0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73,
-     0x42, 0x02, 0x48, 0x03, 0x0a, 0x95, 0x02, 0x0a, 0x30, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x22, 0xcb, 0x01, 0x0a, 0x10, 0x41, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12,
-     0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75,
-     0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x6f,
-     0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x4e, 0x0a, 0x0c,
-     0x62, 0x79, 0x5f, 0x6f, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65,
-     0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x4d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x42, 0x79, 0x4f, 0x6f, 0x6d, 0x53,
-     0x63, 0x6f, 0x72, 0x65, 0x52, 0x0a, 0x62, 0x79, 0x4f, 0x6f, 0x6d, 0x53,
-     0x63, 0x6f, 0x72, 0x65, 0x1a, 0x46, 0x0a, 0x0a, 0x42, 0x79, 0x4f, 0x6f,
-     0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6f,
-     0x6d, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x64, 0x6a, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6f, 0x6f, 0x6d, 0x53, 0x63,
-     0x6f, 0x72, 0x65, 0x41, 0x64, 0x6a, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f,
-     0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63,
-     0x6f, 0x75, 0x6e, 0x74, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xc4, 0x03, 0x0a,
+     0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, 0xab, 0x02, 0x0a, 0x30, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69, 0x6f, 0x6e, 0x5f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+     0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x10, 0x41, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x12, 0x40, 0x0a, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x18,
+     0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x2e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x52,
+     0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x1a, 0x8e, 0x01, 0x0a, 0x06,
+     0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+     0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x76, 0x67, 0x5f, 0x73, 0x69,
+     0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x01, 0x52, 0x0c, 0x61, 0x76, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x42,
+     0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f,
+     0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x53, 0x69, 0x7a,
+     0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61,
+     0x78, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73,
+     0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x53,
+     0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x91, 0x02, 0x0a,
+     0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b,
+     0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xcb, 0x01, 0x0a, 0x10, 0x41,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x4d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+     0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
+     0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+     0x12, 0x4e, 0x0a, 0x0c, 0x62, 0x79, 0x5f, 0x6f, 0x6f, 0x6d, 0x5f, 0x73,
+     0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x4c, 0x6d, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x42, 0x79,
+     0x4f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, 0x0a, 0x62, 0x79,
+     0x4f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x1a, 0x46, 0x0a, 0x0a,
+     0x42, 0x79, 0x4f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x22,
+     0x0a, 0x0d, 0x6f, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f,
+     0x61, 0x64, 0x6a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6f,
+     0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x6a, 0x12, 0x14,
+     0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x0a, 0xc0, 0x03, 0x0a,
      0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
      0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x72, 0x6f,
@@ -391,83 +389,82 @@
      0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x61,
      0x62, 0x6c, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08,
      0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08,
-     0x06, 0x10, 0x07, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x95, 0x04, 0x0a, 0x37,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
-     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f,
-     0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x1a, 0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
-     0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8c, 0x03, 0x0a,
-     0x16, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52,
-     0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12,
-     0x3f, 0x0a, 0x04, 0x6c, 0x6d, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e,
-     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x6d, 0x6b, 0x52, 0x04,
-     0x6c, 0x6d, 0x6b, 0x73, 0x1a, 0x84, 0x01, 0x0a, 0x07, 0x50, 0x72, 0x6f,
-     0x63, 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
-     0x74, 0x61, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12,
-     0x22, 0x0a, 0x0d, 0x6f, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65,
-     0x5f, 0x61, 0x64, 0x6a, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b,
-     0x6f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x6a, 0x12,
-     0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x1a, 0xa9, 0x01, 0x0a, 0x03,
-     0x4c, 0x6d, 0x6b, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6f, 0x6d, 0x5f, 0x73,
-     0x63, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x64, 0x6a, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x05, 0x52, 0x0b, 0x6f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65,
-     0x41, 0x64, 0x6a, 0x12, 0x2f, 0x0a, 0x14, 0x73, 0x79, 0x73, 0x74, 0x65,
-     0x6d, 0x5f, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
-     0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x73,
-     0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x70,
-     0x53, 0x69, 0x7a, 0x65, 0x12, 0x4d, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
-     0x2f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-     0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0xf4, 0x02, 0x0a, 0x35, 0x70, 0x72, 0x6f, 0x74,
+     0x06, 0x10, 0x07, 0x0a, 0x91, 0x04, 0x0a, 0x37, 0x70, 0x72, 0x6f, 0x74,
      0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
      0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x2f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73,
-     0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xa5, 0x02, 0x0a, 0x11, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52,
-     0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x70, 0x6f, 0x77, 0x65,
-     0x72, 0x5f, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c,
-     0x73, 0x2e, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73,
-     0x52, 0x0a, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73,
-     0x1a, 0x4e, 0x0a, 0x0a, 0x45, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x44, 0x61,
-     0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
-     0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d,
-     0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x5f,
-     0x75, 0x77, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x65,
-     0x6e, 0x65, 0x72, 0x67, 0x79, 0x55, 0x77, 0x73, 0x1a, 0x70, 0x0a, 0x0a,
-     0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x12,
-     0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4e, 0x0a, 0x0b, 0x65, 0x6e,
-     0x65, 0x72, 0x67, 0x79, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20,
-     0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61,
-     0x69, 0x6c, 0x73, 0x2e, 0x45, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x44, 0x61,
-     0x74, 0x61, 0x52, 0x0a, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x44, 0x61,
-     0x74, 0x61, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xac, 0x0e, 0x0a, 0x34, 0x70,
+     0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73,
+     0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x36, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
+     0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8c, 0x03, 0x0a, 0x16, 0x41, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f,
+     0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x3f, 0x0a, 0x04, 0x6c,
+     0x6d, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c,
+     0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x4c, 0x6d, 0x6b, 0x52, 0x04, 0x6c, 0x6d, 0x6b, 0x73,
+     0x1a, 0x84, 0x01, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x07,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6f,
+     0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x64, 0x6a,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6f, 0x6f, 0x6d, 0x53,
+     0x63, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x6a, 0x12, 0x12, 0x0a, 0x04, 0x73,
+     0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73,
+     0x69, 0x7a, 0x65, 0x1a, 0xa9, 0x01, 0x0a, 0x03, 0x4c, 0x6d, 0x6b, 0x12,
+     0x22, 0x0a, 0x0d, 0x6f, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65,
+     0x5f, 0x61, 0x64, 0x6a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b,
+     0x6f, 0x6f, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x6a, 0x12,
+     0x2f, 0x0a, 0x14, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x6f,
+     0x6e, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
+     0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x73, 0x79, 0x73, 0x74, 0x65,
+     0x6d, 0x49, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65,
+     0x12, 0x4d, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65,
+     0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b,
+     0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x09, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x0a, 0xf0, 0x02, 0x0a, 0x35,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x6f, 0x77, 0x72,
+     0x61, 0x69, 0x6c, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xa5,
+     0x02, 0x0a, 0x11, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f,
+     0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0b,
+     0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18,
+     0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72,
+     0x52, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52,
+     0x61, 0x69, 0x6c, 0x73, 0x52, 0x0a, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x52,
+     0x61, 0x69, 0x6c, 0x73, 0x1a, 0x4e, 0x0a, 0x0a, 0x45, 0x6e, 0x65, 0x72,
+     0x67, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69,
+     0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+     0x61, 0x6d, 0x70, 0x4d, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x65,
+     0x72, 0x67, 0x79, 0x5f, 0x75, 0x77, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x01, 0x52, 0x09, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x55, 0x77, 0x73,
+     0x1a, 0x70, 0x0a, 0x0a, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69,
+     0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4e,
+     0x0a, 0x0b, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x5f, 0x64, 0x61, 0x74,
+     0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77,
+     0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x6e, 0x65, 0x72,
+     0x67, 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x65, 0x6e, 0x65, 0x72,
+     0x67, 0x79, 0x44, 0x61, 0x74, 0x61, 0x0a, 0xa8, 0x0e, 0x0a, 0x34, 0x70,
      0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
      0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
      0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x74,
@@ -620,268 +617,431 @@
      0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72,
      0x69, 0x63, 0x2e, 0x54, 0x6f, 0x46, 0x69, 0x72, 0x73, 0x74, 0x46, 0x72,
      0x61, 0x6d, 0x65, 0x52, 0x0c, 0x74, 0x6f, 0x46, 0x69, 0x72, 0x73, 0x74,
-     0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xb2, 0x08,
-     0x0a, 0x3c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68, 0x65,
-     0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63,
-     0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x0a, 0xae, 0x08, 0x0a, 0x3c, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70,
+     0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73,
+     0x69, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x1a, 0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x2f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74,
+     0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+     0xa4, 0x07, 0x0a, 0x14, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66,
+     0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73,
+     0x12, 0x5a, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+     0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+     0x32, 0x33, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50,
+     0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69,
+     0x74, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+     0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61,
+     0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x1a, 0x3e, 0x0a, 0x05,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
+     0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+     0x0b, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x4e, 0x61, 0x6d, 0x65,
+     0x1a, 0x8e, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
+     0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63,
+     0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
+     0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f,
+     0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65,
+     0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74,
+     0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64,
+     0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x6c, 0x74,
+     0x61, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x42, 0x79, 0x74, 0x65,
+     0x73, 0x1a, 0xa6, 0x02, 0x0a, 0x08, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69,
+     0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f,
+     0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73,
+     0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x70, 0x61, 0x72,
+     0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x41, 0x0a, 0x05, 0x66,
+     0x72, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f,
+     0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65,
+     0x73, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x66, 0x72, 0x61,
+     0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x0b, 0x73, 0x65, 0x6c, 0x66, 0x5f, 0x61,
+     0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72,
+     0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
+     0x65, 0x73, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52,
+     0x0a, 0x73, 0x65, 0x6c, 0x66, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x12,
+     0x51, 0x0a, 0x0c, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x61, 0x6c, 0x6c,
+     0x6f, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66,
+     0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73,
+     0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0b, 0x63,
+     0x68, 0x69, 0x6c, 0x64, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x1a, 0xb5,
+     0x02, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53,
+     0x74, 0x61, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x21,
+     0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a,
+     0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d,
+     0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x07, 0x70, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 0x73, 0x12, 0x4c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c,
+     0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
+     0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72,
+     0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
+     0x65, 0x73, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x52,
+     0x09, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2e,
+     0x0a, 0x13, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65,
+     0x6c, 0x74, 0x61, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
+     0x44, 0x65, 0x6c, 0x74, 0x61, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e,
+     0x0a, 0x13, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x6f,
+     0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
+     0x54, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x86,
+     0x0f, 0x0a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68,
+     0x77, 0x75, 0x69, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xe3, 0x0d,
+     0x0a, 0x11, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x6e,
+     0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x70,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0e, 0x72, 0x74,
+     0x5f, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x74, 0x43, 0x70,
+     0x75, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x64,
+     0x72, 0x61, 0x77, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x63, 0x6f,
+     0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64,
+     0x72, 0x61, 0x77, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e,
+     0x74, 0x12, 0x24, 0x0a, 0x0e, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x0c, 0x64, 0x72, 0x61, 0x77, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x4d, 0x61, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x64, 0x72, 0x61, 0x77, 0x5f,
+     0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x0c, 0x64, 0x72, 0x61, 0x77, 0x46, 0x72, 0x61,
+     0x6d, 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x64, 0x72, 0x61,
+     0x77, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x76, 0x67, 0x18,
+     0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x64, 0x72, 0x61, 0x77, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x41, 0x76, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x66,
+     0x6c, 0x75, 0x73, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07,
+     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x6c, 0x75, 0x73,
+     0x68, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x08, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a,
+     0x09, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x09,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x4d,
+     0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x5f,
+     0x61, 0x76, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x66,
+     0x6c, 0x75, 0x73, 0x68, 0x41, 0x76, 0x67, 0x12, 0x2c, 0x0a, 0x12, 0x70,
+     0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f,
+     0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x10, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x65, 0x65,
+     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x72, 0x65,
+     0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6d, 0x61,
+     0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x70, 0x72, 0x65,
+     0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x65, 0x65, 0x4d, 0x61, 0x78, 0x12,
+     0x28, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74,
+     0x72, 0x65, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x54, 0x72,
+     0x65, 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x72, 0x65,
+     0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x61, 0x76,
+     0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x70, 0x72, 0x65,
+     0x70, 0x61, 0x72, 0x65, 0x54, 0x72, 0x65, 0x65, 0x41, 0x76, 0x67, 0x12,
+     0x30, 0x0a, 0x14, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c,
+     0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+     0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x67, 0x70, 0x75, 0x43, 0x6f,
+     0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e,
+     0x74, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x6d,
+     0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x18,
+     0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x67, 0x70, 0x75, 0x43, 0x6f,
+     0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x12,
+     0x2c, 0x0a, 0x12, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c,
+     0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x11, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x10, 0x67, 0x70, 0x75, 0x43, 0x6f, 0x6d, 0x70,
+     0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x12, 0x2c, 0x0a,
+     0x12, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
+     0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x76, 0x67, 0x18, 0x12, 0x20, 0x01, 0x28,
+     0x01, 0x52, 0x10, 0x67, 0x70, 0x75, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
+     0x74, 0x69, 0x6f, 0x6e, 0x41, 0x76, 0x67, 0x12, 0x26, 0x0a, 0x0f, 0x75,
+     0x69, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x63, 0x6f, 0x75,
+     0x6e, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x69,
+     0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
+     0x22, 0x0a, 0x0d, 0x75, 0x69, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
+     0x5f, 0x6d, 0x61, 0x78, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
+     0x75, 0x69, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4d, 0x61, 0x78, 0x12,
+     0x22, 0x0a, 0x0d, 0x75, 0x69, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
+     0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
+     0x75, 0x69, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4d, 0x69, 0x6e, 0x12,
+     0x22, 0x0a, 0x0d, 0x75, 0x69, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
+     0x5f, 0x61, 0x76, 0x67, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b,
+     0x75, 0x69, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x41, 0x76, 0x67, 0x12,
+     0x30, 0x0a, 0x14, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f,
+     0x6d, 0x70, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+     0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x73, 0x68, 0x61, 0x64, 0x65,
+     0x72, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e,
+     0x74, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5f,
+     0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+     0x18, 0x18, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x73, 0x68, 0x61, 0x64,
+     0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d,
+     0x65, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5f,
+     0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x5f, 0x61, 0x76, 0x67, 0x18,
+     0x19, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x68, 0x61, 0x64, 0x65,
+     0x72, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x76, 0x67, 0x12,
+     0x26, 0x0a, 0x0f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x68, 0x69, 0x74,
+     0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0d,
+     0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x48, 0x69, 0x74, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65,
+     0x5f, 0x68, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1b, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x61, 0x63, 0x68, 0x65, 0x48, 0x69,
+     0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x61, 0x63,
+     0x68, 0x65, 0x5f, 0x68, 0x69, 0x74, 0x5f, 0x61, 0x76, 0x67, 0x18, 0x1c,
+     0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x63, 0x61, 0x63, 0x68, 0x65, 0x48,
+     0x69, 0x74, 0x41, 0x76, 0x67, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x61, 0x63,
+     0x68, 0x65, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
+     0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x61, 0x63,
+     0x68, 0x65, 0x4d, 0x69, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
+     0x26, 0x0a, 0x0f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6d, 0x69, 0x73,
+     0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4d, 0x69, 0x73, 0x73, 0x54,
+     0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65,
+     0x5f, 0x6d, 0x69, 0x73, 0x73, 0x5f, 0x61, 0x76, 0x67, 0x18, 0x1f, 0x20,
+     0x01, 0x28, 0x01, 0x52, 0x0c, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4d, 0x69,
+     0x73, 0x73, 0x41, 0x76, 0x67, 0x12, 0x2f, 0x0a, 0x14, 0x67, 0x72, 0x61,
+     0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65,
+     0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x20, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x11, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x43, 0x70, 0x75,
+     0x4d, 0x65, 0x6d, 0x4d, 0x61, 0x78, 0x12, 0x2f, 0x0a, 0x14, 0x67, 0x72,
+     0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6d,
+     0x65, 0x6d, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x21, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x11, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x43, 0x70,
+     0x75, 0x4d, 0x65, 0x6d, 0x4d, 0x69, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x67,
+     0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f,
+     0x6d, 0x65, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x18, 0x22, 0x20, 0x01, 0x28,
+     0x01, 0x52, 0x11, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x43,
+     0x70, 0x75, 0x4d, 0x65, 0x6d, 0x41, 0x76, 0x67, 0x12, 0x2f, 0x0a, 0x14,
+     0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x67, 0x70, 0x75,
+     0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x23, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x11, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
+     0x47, 0x70, 0x75, 0x4d, 0x65, 0x6d, 0x4d, 0x61, 0x78, 0x12, 0x2f, 0x0a,
+     0x14, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x67, 0x70,
+     0x75, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x24, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x11, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
+     0x73, 0x47, 0x70, 0x75, 0x4d, 0x65, 0x6d, 0x4d, 0x69, 0x6e, 0x12, 0x2f,
+     0x0a, 0x14, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x67,
+     0x70, 0x75, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x18, 0x25,
+     0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69,
+     0x63, 0x73, 0x47, 0x70, 0x75, 0x4d, 0x65, 0x6d, 0x41, 0x76, 0x67, 0x12,
+     0x26, 0x0a, 0x0f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x6d,
+     0x65, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x26, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x0d, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x4d, 0x65, 0x6d,
+     0x4d, 0x61, 0x78, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x65, 0x78, 0x74, 0x75,
+     0x72, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x27,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72,
+     0x65, 0x4d, 0x65, 0x6d, 0x4d, 0x69, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x74,
+     0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x61,
+     0x76, 0x67, 0x18, 0x28, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x74, 0x65,
+     0x78, 0x74, 0x75, 0x72, 0x65, 0x4d, 0x65, 0x6d, 0x41, 0x76, 0x67, 0x12,
+     0x1e, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d,
+     0x61, 0x78, 0x18, 0x29, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x61, 0x6c,
+     0x6c, 0x4d, 0x65, 0x6d, 0x4d, 0x61, 0x78, 0x12, 0x1e, 0x0a, 0x0b, 0x61,
+     0x6c, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x2a,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x4d, 0x65, 0x6d,
+     0x4d, 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x5f, 0x6d,
+     0x65, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x01,
+     0x52, 0x09, 0x61, 0x6c, 0x6c, 0x4d, 0x65, 0x6d, 0x41, 0x76, 0x67, 0x22,
+     0x5a, 0x0a, 0x11, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x48, 0x77,
+     0x75, 0x69, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x45, 0x0a, 0x0c,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f,
+     0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x6e, 0x64,
+     0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x0a, 0x88, 0x02, 0x0a, 0x32,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x12,
+     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61,
+     0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x61,
+     0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+     0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73,
+     0x74, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x08, 0x70,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x61, 0x0a, 0x07, 0x50,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61,
+     0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+     0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12,
+     0x21, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63,
+     0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x76,
+     0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x0a, 0x9f,
+     0x03, 0x0a, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x75,
+     0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a, 0x61, 0x76, 0x61,
+     0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f,
      0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
      0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x36, 0x70, 0x72, 0x6f,
      0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
      0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
      0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
      0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x22, 0xa4, 0x07, 0x0a, 0x14, 0x48, 0x65, 0x61, 0x70,
-     0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73,
-     0x69, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74,
-     0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01,
+     0x6f, 0x74, 0x6f, 0x22, 0x96, 0x02, 0x0a, 0x13, 0x55, 0x6e, 0x6d, 0x61,
+     0x70, 0x70, 0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x79, 0x6d, 0x62,
+     0x6f, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x01,
      0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48,
-     0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61,
-     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74,
-     0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0d, 0x69,
-     0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
-     0x1a, 0x3e, 0x0a, 0x05, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
-     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x70,
-     0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
-     0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x8e, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74,
-     0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
-     0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
-     0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x6f, 0x75,
-     0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65,
-     0x6c, 0x74, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b,
-     0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
-     0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x74, 0x61,
-     0x42, 0x79, 0x74, 0x65, 0x73, 0x1a, 0xa6, 0x02, 0x0a, 0x08, 0x43, 0x61,
-     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61,
-     0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x68, 0x61,
-     0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
-     0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12,
-     0x41, 0x0a, 0x05, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61,
-     0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c,
-     0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52,
-     0x05, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x0b, 0x73, 0x65,
-     0x6c, 0x66, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65,
-     0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c,
-     0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-     0x65, 0x72, 0x73, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x66, 0x41, 0x6c, 0x6c,
-     0x6f, 0x63, 0x73, 0x12, 0x51, 0x0a, 0x0c, 0x63, 0x68, 0x69, 0x6c, 0x64,
-     0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70,
-     0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73,
-     0x69, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x73, 0x52, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x41, 0x6c, 0x6c, 0x6f,
-     0x63, 0x73, 0x1a, 0xb5, 0x02, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61,
-     0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
-     0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70,
-     0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
-     0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d,
-     0x65, 0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-     0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55,
+     0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53,
+     0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x52, 0x0e, 0x70,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+     0x73, 0x1a, 0xa0, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73,
+     0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x52, 0x0a, 0x10,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+     0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64,
+     0x61, 0x74, 0x61, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09,
+     0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+     0x03, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d,
+     0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6e,
+     0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x66,
+     0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x0a, 0xfc, 0x01, 0x0a,
+     0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x75, 0x6e, 0x73,
+     0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x22, 0xad, 0x01, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x79,
+     0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73,
+     0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72,
      0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
-     0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4c, 0x0a, 0x09,
-     0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20,
-     0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65,
-     0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c,
-     0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x73,
-     0x69, 0x74, 0x65, 0x52, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
-     0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
-     0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x62, 0x79, 0x74, 0x65,
-     0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x70, 0x72, 0x6f,
-     0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x42, 0x79, 0x74,
-     0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
-     0x65, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65,
-     0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x70, 0x72, 0x6f,
-     0x66, 0x69, 0x6c, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74,
-     0x65, 0x73, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x8c, 0x02, 0x0a, 0x32, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x61,
-     0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x12, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67,
-     0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x61, 0x63,
-     0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
-     0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74,
-     0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x08, 0x70, 0x61,
-     0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x61, 0x0a, 0x07, 0x50, 0x61,
-     0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63,
-     0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
-     0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x21,
-     0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f,
-     0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x76, 0x65,
-     0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x02, 0x48,
-     0x03, 0x0a, 0xa3, 0x03, 0x0a, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65,
+     0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x52, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x54, 0x0a,
+     0x05, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x6f,
+     0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+     0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75,
+     0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+     0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x18, 0x0a,
+     0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a,
+     0xf8, 0x03, 0x0a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+     0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74,
+     0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x1a, 0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+     0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5,
+     0x02, 0x0a, 0x0d, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53,
+     0x74, 0x61, 0x74, 0x73, 0x12, 0x53, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74,
+     0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a,
+     0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73,
+     0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61,
+     0x74, 0x73, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+     0x53, 0x74, 0x61, 0x74, 0x73, 0x1a, 0x65, 0x0a, 0x06, 0x53, 0x61, 0x6d,
+     0x70, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68,
+     0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x08, 0x68, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65,
+     0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c,
+     0x65, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
+     0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63, 0x68,
+     0x61, 0x62, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65,
+     0x1a, 0xa7, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+     0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x70,
+     0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x75, 0x70,
+     0x69, 0x64, 0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
+     0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+     0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x3f, 0x0a,
+     0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
+     0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76,
+     0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x53,
+     0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c,
+     0x65, 0x73, 0x0a, 0xbb, 0x14, 0x0a, 0x25, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x1a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x62,
+     0x61, 0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
      0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
      0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x2f, 0x75, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a,
-     0x61, 0x76, 0x61, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x36,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
-     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x02, 0x0a, 0x13, 0x55,
-     0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53,
-     0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x0f, 0x70, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
-     0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61,
-     0x76, 0x61, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x2e, 0x50, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
-     0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d,
-     0x62, 0x6f, 0x6c, 0x73, 0x1a, 0xa0, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f,
-     0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12,
-     0x52, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d,
-     0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65,
-     0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
-     0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
-     0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65,
-     0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c,
-     0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
-     0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0x80, 0x02, 0x0a, 0x39, 0x70, 0x72, 0x6f, 0x74,
+     0x64, 0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74,
      0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
      0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x2f, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
-     0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xad,
-     0x01, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69,
-     0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x41, 0x0a,
-     0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x29, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x6e, 0x73, 0x79,
-     0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x73, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x66, 0x72,
-     0x61, 0x6d, 0x65, 0x73, 0x1a, 0x54, 0x0a, 0x05, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-     0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69,
-     0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x69,
-     0x6c, 0x64, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
-     0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61,
-     0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xfc,
-     0x03, 0x0a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6a,
-     0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61,
-     0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x1a, 0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
-     0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x02,
-     0x0a, 0x0d, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74,
-     0x61, 0x74, 0x73, 0x12, 0x53, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61,
-     0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20,
-     0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61,
-     0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
-     0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74,
-     0x73, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53,
-     0x74, 0x61, 0x74, 0x73, 0x1a, 0x65, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70,
-     0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x65,
-     0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x08, 0x68, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12,
-     0x2e, 0x0a, 0x13, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65,
-     0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61,
-     0x62, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x1a,
-     0xa7, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
-     0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x70, 0x69,
-     0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x75, 0x70, 0x69,
-     0x64, 0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
-     0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x3f, 0x0a, 0x07,
-     0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61,
-     0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x53, 0x61,
-     0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65,
-     0x73, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x8e, 0x13, 0x0a, 0x25, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
-     0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x2f, 0x62, 0x61, 0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
-     0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d,
-     0x5f, 0x75, 0x6e, 0x61, 0x67, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x6d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
-     0x37, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b,
-     0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x35, 0x70, 0x72,
+     0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36, 0x70, 0x72,
      0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69,
-     0x6c, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x2f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3c, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68, 0x65, 0x61, 0x70, 0x5f,
-     0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c,
-     0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x75, 0x6e,
+     0x61, 0x67, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x2f, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x37, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x72, 0x65,
+     0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x2f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x5f,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x1a, 0x34, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x74,
+     0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3c, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x2f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f,
+     0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
+     0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x31, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68, 0x77, 0x75, 0x69, 0x5f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
      0x32, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
      0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x61, 0x63,
@@ -900,8 +1060,8 @@
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
      0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
      0x2f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
-     0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf8,
-     0x02, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61,
+     0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa2,
+     0x03, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61,
      0x64, 0x61, 0x74, 0x61, 0x12, 0x50, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f,
      0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72,
      0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65,
@@ -926,117 +1086,127 @@
      0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
      0x52, 0x1e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x54, 0x72, 0x69, 0x67,
      0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
-     0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x1a, 0x43, 0x0a, 0x05,
-     0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
-     0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-     0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x78, 0x18, 0x02, 0x20, 0x01,
-     0x28, 0x0d, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x76,
-     0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
-     0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xda, 0x09, 0x0a, 0x0c, 0x54, 0x72,
-     0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x48,
-     0x0a, 0x0c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x62, 0x61,
-     0x74, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42, 0x61,
-     0x74, 0x74, 0x65, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52,
-     0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42, 0x61, 0x74, 0x74,
-     0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f,
-     0x63, 0x70, 0x75, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43,
-     0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x12, 0x45, 0x0a, 0x0b,
-     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72,
-     0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x12, 0x5c, 0x0a, 0x11, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x75,
-     0x6e, 0x61, 0x67, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x6e, 0x61, 0x67, 0x67, 0x72,
-     0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d,
-     0x55, 0x6e, 0x61, 0x67, 0x67, 0x12, 0x55, 0x0a, 0x14, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
-     0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74,
-     0x52, 0x12, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63,
-     0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0b,
-     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x69, 0x6f, 0x6e, 0x18,
-     0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x49, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x5f, 0x6c, 0x6d, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b,
-     0x12, 0x4d, 0x0a, 0x10, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f,
-     0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10,
+     0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62,
+     0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e,
+     0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74,
+     0x65, 0x73, 0x1a, 0x43, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+     0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x69,
+     0x64, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x69, 0x64,
+     0x78, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
+     0xae, 0x0a, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, 0x0c, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x74, 0x18, 0x05, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69,
-     0x6c, 0x73, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50,
-     0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
-     0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74,
-     0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52,
-     0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72,
-     0x74, 0x75, 0x70, 0x12, 0x5b, 0x0a, 0x16, 0x68, 0x65, 0x61, 0x70, 0x5f,
-     0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c,
-     0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72,
-     0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
-     0x65, 0x73, 0x52, 0x14, 0x68, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66,
-     0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73,
-     0x12, 0x45, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x65,
-     0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
-     0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
-     0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x74, 0x72,
-     0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
-     0x54, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69,
-     0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0f,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55,
-     0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x12, 0x75, 0x6e, 0x73, 0x79, 0x6d,
-     0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65,
-     0x73, 0x12, 0x46, 0x0a, 0x0f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65,
-     0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76,
-     0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0d,
-     0x6a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74,
-     0x73, 0x12, 0x55, 0x0a, 0x12, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x5f, 0x6c, 0x6d, 0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18,
-     0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65,
-     0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10,
-     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65,
-     0x61, 0x73, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x15, 0x75, 0x6e, 0x6d, 0x61,
-     0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x73, 0x79,
-     0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x72, 0x6f, 0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x4d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x06, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43,
+     0x70, 0x75, 0x12, 0x45, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
      0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70,
-     0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
-     0x73, 0x52, 0x13, 0x75, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a,
-     0x61, 0x76, 0x61, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x2a, 0x06,
-     0x08, 0xc2, 0x03, 0x10, 0xf4, 0x03, 0x2a, 0x06, 0x08, 0xf4, 0x03, 0x10,
-     0xe9, 0x07, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x0a,
-     0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e,
-     0x10, 0x0f, 0x42, 0x02, 0x48, 0x03}};
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65,
+     0x6d, 0x12, 0x5c, 0x0a, 0x11, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x75, 0x6e, 0x61, 0x67, 0x67, 0x18, 0x0b,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
+     0x55, 0x6e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x55, 0x6e, 0x61, 0x67, 0x67, 0x12,
+     0x55, 0x0a, 0x14, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x70,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
+     0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61,
+     0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x12, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69,
+     0x73, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x5f, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x49, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x12, 0x42,
+     0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6c, 0x6d,
+     0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x12, 0x4d, 0x0a, 0x10, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69,
+     0x6c, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f,
+     0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0f, 0x61, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c,
+     0x73, 0x12, 0x4e, 0x0a, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x5b, 0x0a,
+     0x16, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
+     0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x18,
+     0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43,
+     0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x52, 0x14, 0x68, 0x65,
+     0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c,
+     0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x74, 0x72,
+     0x61, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+     0x74, 0x61, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74,
+     0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x13, 0x75, 0x6e, 0x73,
+     0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f,
+     0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x52,
+     0x12, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65,
+     0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x0f, 0x6a,
+     0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53,
+     0x74, 0x61, 0x74, 0x73, 0x52, 0x0d, 0x6a, 0x61, 0x76, 0x61, 0x48, 0x65,
+     0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x55, 0x0a, 0x12, 0x61,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6c, 0x6d, 0x6b, 0x5f, 0x72,
+     0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x58,
+     0x0a, 0x15, 0x75, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a,
+     0x61, 0x76, 0x61, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18,
+     0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61, 0x76, 0x61,
+     0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x52, 0x13, 0x75, 0x6e, 0x6d,
+     0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x79, 0x6d,
+     0x62, 0x6f, 0x6c, 0x73, 0x12, 0x52, 0x0a, 0x13, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x5f, 0x68, 0x77, 0x75, 0x69, 0x5f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x48,
+     0x77, 0x75, 0x69, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x11, 0x61,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x48, 0x77, 0x75, 0x69, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x2a, 0x06, 0x08, 0xc2, 0x03, 0x10, 0xf4, 0x03,
+     0x2a, 0x06, 0x08, 0xf4, 0x03, 0x10, 0xe9, 0x07, 0x4a, 0x04, 0x08, 0x04,
+     0x10, 0x05, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0d,
+     0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f}};
 
 }  // namespace perfetto
 
diff --git a/src/trace_processor/metrics/trace_metadata.sql b/src/trace_processor/metrics/trace_metadata.sql
index 9da756a..c7e68e8 100644
--- a/src/trace_processor/metrics/trace_metadata.sql
+++ b/src/trace_processor/metrics/trace_metadata.sql
@@ -34,5 +34,9 @@
   'statsd_triggering_subscription_id', (
     SELECT int_value FROM metadata
     WHERE name = 'statsd_triggering_subscription_id'
+  ),
+ 'trace_size_bytes', (
+    SELECT int_value FROM metadata
+    WHERE name = 'trace_size_bytes'
   )
 );
diff --git a/src/trace_processor/process_table.cc b/src/trace_processor/process_table.cc
deleted file mode 100644
index a25c0fa..0000000
--- a/src/trace_processor/process_table.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2018 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/trace_processor/process_table.h"
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/sqlite/query_constraints.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-
-using namespace sqlite_utils;
-
-}  // namespace
-
-ProcessTable::ProcessTable(sqlite3*, const TraceStorage* storage)
-    : storage_(storage) {}
-
-void ProcessTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<ProcessTable>(db, storage, "process");
-}
-
-util::Status ProcessTable::Init(int, const char* const*, Schema* schema) {
-  *schema = Schema(
-      {
-          SqliteTable::Column(Column::kUpid, "upid", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kName, "name", SqlValue::Type::kString),
-          SqliteTable::Column(Column::kPid, "pid", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kStartTs, "start_ts",
-                              SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kEndTs, "end_ts", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kParentUpid, "parent_upid",
-                              SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kUid, "uid", SqlValue::Type::kLong),
-      },
-      {Column::kUpid});
-  return util::OkStatus();
-}
-
-std::unique_ptr<SqliteTable::Cursor> ProcessTable::CreateCursor() {
-  return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
-}
-
-int ProcessTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
-  // If the query has a constraint on the |upid| field, return a reduced cost
-  // because we can do that filter efficiently.
-  const auto& cs = qc.constraints();
-  auto fn = [](const QueryConstraints::Constraint& c) {
-    return c.column == Column::kUpid && sqlite_utils::IsOpEq(c.op);
-  };
-  info->estimated_cost = std::find_if(cs.begin(), cs.end(), fn) != cs.end()
-                             ? 1
-                             : static_cast<uint32_t>(storage_->process_count());
-  return SQLITE_OK;
-}
-
-ProcessTable::Cursor::Cursor(ProcessTable* table)
-    : SqliteTable::Cursor(table), storage_(table->storage_) {}
-
-int ProcessTable::Cursor::Filter(const QueryConstraints& qc,
-                                 sqlite3_value** argv,
-                                 FilterHistory) {
-  min_ = 0;
-  max_ = static_cast<uint32_t>(storage_->process_count());
-  desc_ = false;
-
-  for (size_t j = 0; j < qc.constraints().size(); j++) {
-    const auto& cs = qc.constraints()[j];
-    if (cs.column == Column::kUpid) {
-      auto constraint_upid = static_cast<UniquePid>(sqlite3_value_int(argv[j]));
-      // Set the range of upids that we are interested in, based on the
-      // constraints in the query. Everything between min and max (exclusive)
-      // will be returned.
-      if (IsOpEq(cs.op)) {
-        min_ = constraint_upid;
-        max_ = constraint_upid + 1;
-      } else if (IsOpGe(cs.op) || IsOpGt(cs.op)) {
-        min_ = IsOpGt(cs.op) ? constraint_upid + 1 : constraint_upid;
-      } else if (IsOpLe(cs.op) || IsOpLt(cs.op)) {
-        max_ = IsOpLt(cs.op) ? constraint_upid : constraint_upid + 1;
-      }
-    }
-  }
-
-  for (const auto& ob : qc.order_by()) {
-    if (ob.iColumn == Column::kUpid) {
-      desc_ = ob.desc;
-    }
-  }
-  index_ = 0;
-
-  return SQLITE_OK;
-}
-
-int ProcessTable::Cursor::Column(sqlite3_context* context, int N) {
-  uint32_t current = desc_ ? max_ - index_ - 1 : min_ + index_;
-  const auto& process = storage_->GetProcess(current);
-  switch (N) {
-    case Column::kUpid: {
-      sqlite3_result_int64(context, current);
-      break;
-    }
-    case Column::kName: {
-      const auto& name = storage_->GetString(process.name_id);
-      sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic);
-      break;
-    }
-    case Column::kPid: {
-      sqlite3_result_int64(context, process.pid);
-      break;
-    }
-    case Column::kStartTs: {
-      if (process.start_ns != 0) {
-        sqlite3_result_int64(context, process.start_ns);
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    case Column::kEndTs: {
-      if (process.end_ns != 0) {
-        sqlite3_result_int64(context, process.end_ns);
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    case Column::kParentUpid: {
-      if (process.parent_upid.has_value()) {
-        sqlite3_result_int64(context, process.parent_upid.value());
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    case Column::kUid: {
-      if (process.uid.has_value()) {
-        sqlite3_result_int64(context, process.uid.value());
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    default:
-      PERFETTO_FATAL("Unknown column %d", N);
-      break;
-  }
-  return SQLITE_OK;
-}
-
-int ProcessTable::Cursor::Next() {
-  ++index_;
-  return SQLITE_OK;
-}
-
-int ProcessTable::Cursor::Eof() {
-  return index_ >= (max_ - min_);
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/process_table.h b/src/trace_processor/process_table.h
deleted file mode 100644
index 7e661a6..0000000
--- a/src/trace_processor/process_table.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 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_TRACE_PROCESSOR_PROCESS_TABLE_H_
-#define SRC_TRACE_PROCESSOR_PROCESS_TABLE_H_
-
-#include <limits>
-#include <memory>
-
-#include "src/trace_processor/sqlite/sqlite_table.h"
-#include "src/trace_processor/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// The implementation of the SQLite table containing each unique process with
-// their details.
-class ProcessTable : public SqliteTable {
- public:
-  enum Column {
-    kUpid = 0,
-    kName = 1,
-    kPid = 2,
-    kStartTs = 3,
-    kEndTs = 4,
-    kParentUpid = 5,
-    kUid = 6
-  };
-  class Cursor : public SqliteTable::Cursor {
-   public:
-    Cursor(ProcessTable*);
-
-    // Implementation of Table::Cursor.
-    int Filter(const QueryConstraints&,
-               sqlite3_value**,
-               FilterHistory) override;
-    int Next() override;
-    int Eof() override;
-    int Column(sqlite3_context*, int N) override;
-
-   private:
-    const TraceStorage* const storage_;
-    UniquePid min_ = 0;
-    UniquePid max_ = 0;
-    uint32_t index_ = 0;
-    bool desc_ = false;
-  };
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  ProcessTable(sqlite3*, const TraceStorage*);
-
-  // Table implementation.
-  util::Status Init(int, const char* const*, SqliteTable::Schema*) override;
-  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
- private:
-  const TraceStorage* const storage_;
-};
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_PROCESS_TABLE_H_
diff --git a/src/trace_processor/process_table_unittest.cc b/src/trace_processor/process_table_unittest.cc
deleted file mode 100644
index 84de696..0000000
--- a/src/trace_processor/process_table_unittest.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2018 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/trace_processor/process_table.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/trace_processor_context.h"
-
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-class ProcessTableUnittest : public ::testing::Test {
- public:
-  ProcessTableUnittest() {
-    sqlite3* db = nullptr;
-    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
-    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
-    db_.reset(db);
-
-    context_.storage.reset(new TraceStorage());
-    context_.process_tracker.reset(new ProcessTracker(&context_));
-
-    ProcessTable::RegisterTable(db_.get(), context_.storage.get());
-  }
-
-  void PrepareValidStatement(const std::string& sql) {
-    int size = static_cast<int>(sql.size());
-    sqlite3_stmt* stmt;
-    ASSERT_EQ(sqlite3_prepare_v2(*db_, sql.c_str(), size, &stmt, nullptr),
-              SQLITE_OK);
-    stmt_.reset(stmt);
-  }
-
-  const char* GetColumnAsText(int colId) {
-    return reinterpret_cast<const char*>(sqlite3_column_text(*stmt_, colId));
-  }
-
- protected:
-  TraceProcessorContext context_;
-  ScopedDb db_;
-  ScopedStmt stmt_;
-};
-
-TEST_F(ProcessTableUnittest, SelectUpidAndName) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement("SELECT upid, name FROM process");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 0 /* upid */);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ProcessTableUnittest, SelectUpidAndNameWithFilter) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement("SELECT upid, name FROM process where upid = 2");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ProcessTableUnittest, SelectUpidAndNameWithOrder) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement("SELECT upid, name FROM process ORDER BY upid desc");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 0 /* upid */);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ProcessTableUnittest, SelectUpidAndNameFilterGt) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement("SELECT upid, name FROM process where upid > 1");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ProcessTableUnittest, SelectUpidAndNameFilterName) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement(
-      "SELECT upid, name FROM process where name = \"process2\"");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ProcessTableUnittest, SelectUpidAndNameFilterDifferentOr) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement(
-      "SELECT upid, name FROM process where upid = 2 or name = \"process2\"");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ProcessTableUnittest, SelectUpidAndNameFilterSameOr) {
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  context_.process_tracker->SetProcessMetadata(1, base::nullopt, kCommProc1);
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, kCommProc2);
-
-  PrepareValidStatement(
-      "SELECT upid, name FROM process where upid = 1 or upid = 2");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index 5a7ef6b..27668ef 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -33,158 +33,205 @@
 
 ProcessTracker::~ProcessTracker() = default;
 
-UniqueTid ProcessTracker::StartNewThread(int64_t timestamp,
+UniqueTid ProcessTracker::StartNewThread(base::Optional<int64_t> timestamp,
                                          uint32_t tid,
                                          StringId thread_name_id) {
-  UniqueTid new_utid = context_->storage->AddEmptyThread(tid);
-  TraceStorage::Thread* thread = context_->storage->GetMutableThread(new_utid);
-  thread->name_id = thread_name_id;
-  thread->start_ns = timestamp;
+  tables::ThreadTable::Row row;
+  row.tid = tid;
+  row.name = thread_name_id;
+  row.start_ts = timestamp;
+
+  auto* thread_table = context_->storage->mutable_thread_table();
+  UniqueTid new_utid = thread_table->Insert(row).value;
   tids_[tid].emplace_back(new_utid);
   return new_utid;
 }
 
 void ProcessTracker::EndThread(int64_t timestamp, uint32_t tid) {
+  auto* thread_table = context_->storage->mutable_thread_table();
+  auto* process_table = context_->storage->mutable_process_table();
+
   UniqueTid utid = GetOrCreateThread(tid);
-  TraceStorage::Thread* thread = context_->storage->GetMutableThread(utid);
-  thread->end_ns = timestamp;
+  thread_table->mutable_end_ts()->Set(utid, timestamp);
 
   // Remove the thread from the list of threads being tracked as any event after
   // this one should be ignored.
   auto& vector = tids_[tid];
   vector.erase(std::remove(vector.begin(), vector.end(), utid));
 
-  if (thread->upid.has_value()) {
-    TraceStorage::Process* process =
-        context_->storage->GetMutableProcess(thread->upid.value());
-
+  auto opt_upid = thread_table->upid()[utid];
+  if (opt_upid.has_value()) {
     // If the process pid and thread tid are equal, then this is the main thread
     // of the process.
-    if (process->pid == thread->tid) {
-      process->end_ns = timestamp;
+    if (process_table->pid()[*opt_upid] == thread_table->tid()[utid]) {
+      process_table->mutable_end_ts()->Set(*opt_upid, timestamp);
     }
   }
 }
 
 base::Optional<UniqueTid> ProcessTracker::GetThreadOrNull(uint32_t tid) {
-  auto vector_it = tids_.find(tid);
-  if (vector_it == tids_.end() || vector_it->second.empty()) {
+  auto opt_utid = GetThreadOrNull(tid, base::nullopt);
+  if (!opt_utid)
     return base::nullopt;
-  }
+
+  auto* threads = context_->storage->mutable_thread_table();
+  UniqueTid utid = *opt_utid;
+
+  // Ensure that the tid matches the tid we were looking for.
+  PERFETTO_DCHECK(threads->tid()[utid] == tid);
 
   // If the thread is being tracked by the process tracker, it should not be
   // known to have ended.
-  UniqueTid utid = vector_it->second.back();
-  PERFETTO_DCHECK(context_->storage->GetMutableThread(utid)->end_ns == 0u);
+  PERFETTO_DCHECK(!threads->end_ts()[utid].has_value());
+
   return utid;
 }
 
 UniqueTid ProcessTracker::GetOrCreateThread(uint32_t tid) {
   auto utid = GetThreadOrNull(tid);
-  return utid ? utid.value() : StartNewThread(0, tid, 0);
+  return utid ? *utid : StartNewThread(base::nullopt, tid, kNullStringId);
 }
 
 UniqueTid ProcessTracker::UpdateThreadName(uint32_t tid,
                                            StringId thread_name_id) {
+  auto* thread_table = context_->storage->mutable_thread_table();
   auto utid = GetOrCreateThread(tid);
-  if (!thread_name_id.is_null()) {
-    auto* thread = context_->storage->GetMutableThread(utid);
-    thread->name_id = thread_name_id;
-  }
+  if (!thread_name_id.is_null())
+    thread_table->mutable_name()->Set(utid, thread_name_id);
   return utid;
 }
 
 void ProcessTracker::SetThreadNameIfUnset(UniqueTid utid,
                                           StringId thread_name_id) {
-  auto* thread = context_->storage->GetMutableThread(utid);
-  if (thread->name_id == kNullStringId)
-    thread->name_id = thread_name_id;
+  auto* thread_table = context_->storage->mutable_thread_table();
+  if (thread_table->name()[utid].is_null())
+    thread_table->mutable_name()->Set(utid, thread_name_id);
+}
+
+bool ProcessTracker::IsThreadAlive(UniqueTid utid) {
+  auto* threads = context_->storage->mutable_thread_table();
+  auto* processes = context_->storage->mutable_process_table();
+
+  // If the thread has an end ts, it's certainly dead.
+  if (threads->end_ts()[utid].has_value())
+    return false;
+
+  // If we don't know the parent process, we have to consider this thread alive.
+  auto opt_current_upid = threads->upid()[utid];
+  if (!opt_current_upid)
+    return true;
+
+  // If the process is already dead, the thread can't be alive.
+  UniquePid current_upid = *opt_current_upid;
+  if (processes->end_ts()[current_upid].has_value())
+    return false;
+
+  // If the process has been replaced in |pids_|, this thread is dead.
+  uint32_t current_pid = processes->pid()[current_upid];
+  auto pid_it = pids_.find(current_pid);
+  if (pid_it != pids_.end() && pid_it->second != current_upid)
+    return false;
+
+  return true;
+}
+
+base::Optional<UniqueTid> ProcessTracker::GetThreadOrNull(
+    uint32_t tid,
+    base::Optional<uint32_t> pid) {
+  auto* threads = context_->storage->mutable_thread_table();
+  auto* processes = context_->storage->mutable_process_table();
+
+  auto vector_it = tids_.find(tid);
+  if (vector_it == tids_.end())
+    return base::nullopt;
+
+  // Iterate backwards through the threads so ones later in the trace are more
+  // likely to be picked.
+  const auto& vector = vector_it->second;
+  for (auto it = vector.rbegin(); it != vector.rend(); it++) {
+    UniqueTid current_utid = *it;
+
+    // If we finished this thread, we should have removed it from the vector
+    // entirely.
+    PERFETTO_DCHECK(!threads->end_ts()[current_utid].has_value());
+
+    // If the thread is dead, ignore it.
+    if (!IsThreadAlive(current_utid))
+      continue;
+
+    // If we don't know the parent process, we have to choose this thread.
+    auto opt_current_upid = threads->upid()[current_utid];
+    if (!opt_current_upid)
+      return current_utid;
+
+    // We found a thread that matches both the tid and its parent pid.
+    uint32_t current_pid = processes->pid()[*opt_current_upid];
+    if (!pid || current_pid == *pid)
+      return current_utid;
+  }
+  return base::nullopt;
 }
 
 UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
-  auto vector_it = tids_.find(tid);
+  auto* thread_table = context_->storage->mutable_thread_table();
 
   // Try looking for a thread that matches both tid and thread group id (pid).
-  TraceStorage::Thread* thread = nullptr;
-  UniqueTid utid = 0;
-  if (vector_it != tids_.end()) {
-    const auto& vector = vector_it->second;
-
-    // Iterate backwards through the threads so ones later in the trace are more
-    // likely to be picked.
-    for (auto it = vector.rbegin(); it != vector.rend(); it++) {
-      auto* iter_thread = context_->storage->GetMutableThread(*it);
-
-      // If we finished this thread, we should have removed it from the vector
-      // entirely.
-      PERFETTO_DCHECK(iter_thread->end_ns == 0);
-
-      if (!iter_thread->upid.has_value()) {
-        // We haven't discovered the parent process for the thread. Assign it
-        // now and use this thread.
-        thread = iter_thread;
-        utid = *it;
-        break;
-      }
-
-      const auto& iter_process =
-          context_->storage->GetProcess(iter_thread->upid.value());
-      if (iter_process.end_ns != 0) {
-        // If the process is already dead, don't bother choosing the associated
-        // thread.
-        continue;
-      }
-      if (iter_process.pid == pid) {
-        // We found a thread that matches both the tid and its parent pid.
-        thread = iter_thread;
-        utid = *it;
-        break;
-      }
-    }  // for(tids).
-  }
+  base::Optional<UniqueTid> opt_utid = GetThreadOrNull(tid, pid);
 
   // If no matching thread was found, create a new one.
-  if (thread == nullptr) {
-    utid = StartNewThread(0, tid, 0);
-    thread = context_->storage->GetMutableThread(utid);
-  }
+  UniqueTid utid =
+      opt_utid ? *opt_utid : StartNewThread(base::nullopt, tid, kNullStringId);
+  PERFETTO_DCHECK(thread_table->tid()[utid] == tid);
 
   // Find matching process or create new one.
-  if (!thread->upid.has_value()) {
-    thread->upid = GetOrCreateProcess(pid);
+  if (!thread_table->upid()[utid].has_value()) {
+    thread_table->mutable_upid()->Set(utid, GetOrCreateProcess(pid));
   }
 
-  ResolvePendingAssociations(utid, *thread->upid);
+  ResolvePendingAssociations(utid, *thread_table->upid()[utid]);
 
   return utid;
 }
 
-UniquePid ProcessTracker::StartNewProcess(int64_t timestamp,
-                                          uint32_t parent_tid,
+UniquePid ProcessTracker::StartNewProcess(base::Optional<int64_t> timestamp,
+                                          base::Optional<uint32_t> parent_tid,
                                           uint32_t pid,
                                           StringId main_thread_name) {
   pids_.erase(pid);
+  // TODO(eseckler): Consider erasing all old entries in |tids_| that match the
+  // |pid| (those would be for an older process with the same pid). Right now,
+  // we keep them in |tids_| (if they weren't erased by EndThread()), but ignore
+  // them in GetThreadOrNull().
 
   // Create a new UTID for the main thread, so we don't end up reusing an old
   // entry in case of TID recycling.
-  StartNewThread(timestamp, /*tid=*/pid, 0);
+  StartNewThread(timestamp, /*tid=*/pid, kNullStringId);
 
   // Note that we erased the pid above so this should always return a new
   // process.
-  std::pair<UniquePid, TraceStorage::Process*> process =
-      GetOrCreateProcessPtr(pid);
-  PERFETTO_DCHECK(process.second->name_id == 0);
-  process.second->start_ns = timestamp;
-  process.second->name_id = main_thread_name;
+  UniquePid upid = GetOrCreateProcess(pid);
 
-  UniqueTid parent_utid = GetOrCreateThread(parent_tid);
-  auto* parent_thread = context_->storage->GetMutableThread(parent_utid);
-  if (parent_thread->upid.has_value()) {
-    process.second->parent_upid = parent_thread->upid.value();
-  } else {
-    pending_parent_assocs_.emplace_back(parent_utid, process.first);
+  auto* process_table = context_->storage->mutable_process_table();
+  auto* thread_table = context_->storage->mutable_thread_table();
+
+  PERFETTO_DCHECK(process_table->name()[upid].is_null());
+  PERFETTO_DCHECK(!process_table->start_ts()[upid].has_value());
+
+  if (timestamp) {
+    process_table->mutable_start_ts()->Set(upid, *timestamp);
   }
-  return process.first;
+  process_table->mutable_name()->Set(upid, main_thread_name);
+
+  if (parent_tid) {
+    UniqueTid parent_utid = GetOrCreateThread(*parent_tid);
+    auto opt_parent_upid = thread_table->upid()[parent_utid];
+    if (opt_parent_upid.has_value()) {
+      process_table->mutable_parent_upid()->Set(upid, *opt_parent_upid);
+    } else {
+      pending_parent_assocs_.emplace_back(parent_utid, upid);
+    }
+  }
+  return upid;
 }
 
 UniquePid ProcessTracker::SetProcessMetadata(uint32_t pid,
@@ -196,49 +243,54 @@
   if (ppid.has_value()) {
     pupid = GetOrCreateProcess(ppid.value());
   }
-  UniquePid upid;
-  TraceStorage::Process* process;
-  std::tie(upid, process) = GetOrCreateProcessPtr(pid);
-  process->name_id = proc_name_id;
-  process->parent_upid = pupid;
+
+  UniquePid upid = GetOrCreateProcess(pid);
+
+  auto* process_table = context_->storage->mutable_process_table();
+  process_table->mutable_name()->Set(upid, proc_name_id);
+
+  if (pupid)
+    process_table->mutable_parent_upid()->Set(upid, *pupid);
+
   return upid;
 }
 
 void ProcessTracker::SetProcessUid(UniquePid upid, uint32_t uid) {
-  context_->storage->GetMutableProcess(upid)->uid = uid;
+  auto* process_table = context_->storage->mutable_process_table();
+  process_table->mutable_uid()->Set(upid, uid);
 }
 
 void ProcessTracker::SetProcessNameIfUnset(UniquePid upid,
                                            StringId process_name_id) {
-  TraceStorage::Process* process = context_->storage->GetMutableProcess(upid);
-  if (process->name_id == kNullStringId)
-    process->name_id = process_name_id;
+  auto* process_table = context_->storage->mutable_process_table();
+  if (process_table->name()[upid].is_null())
+    process_table->mutable_name()->Set(upid, process_name_id);
 }
 
 void ProcessTracker::UpdateProcessNameFromThreadName(uint32_t tid,
                                                      StringId thread_name) {
+  auto* thread_table = context_->storage->mutable_thread_table();
+  auto* process_table = context_->storage->mutable_process_table();
+
   auto utid = GetOrCreateThread(tid);
-  TraceStorage::Thread* thread = context_->storage->GetMutableThread(utid);
-  if (thread->upid.has_value()) {
-    auto* process = context_->storage->GetMutableProcess(thread->upid.value());
-    if (process->pid == tid) {
-      process->name_id = thread_name;
+  auto opt_upid = thread_table->upid()[utid];
+  if (opt_upid.has_value()) {
+    if (process_table->pid()[*opt_upid] == tid) {
+      process_table->mutable_name()->Set(*opt_upid, thread_name);
     }
   }
 }
 
 UniquePid ProcessTracker::GetOrCreateProcess(uint32_t pid) {
-  return GetOrCreateProcessPtr(pid).first;
-}
-
-std::pair<UniquePid, TraceStorage::Process*>
-ProcessTracker::GetOrCreateProcessPtr(uint32_t pid) {
   UniquePid upid;
   auto it = pids_.find(pid);
   if (it != pids_.end()) {
     upid = it->second;
   } else {
-    upid = context_->storage->AddEmptyProcess(pid);
+    tables::ProcessTable::Row row;
+    row.pid = pid;
+    upid = context_->storage->mutable_process_table()->Insert(row).value;
+
     pids_.emplace(pid, upid);
 
     // Create an entry for the main thread.
@@ -247,33 +299,35 @@
     // call. This call usually comes from the ProcessTree dump which is delayed.
     UpdateThread(/*tid=*/pid, pid);
   }
-  return std::make_pair(upid, context_->storage->GetMutableProcess(upid));
+  return upid;
 }
 
 void ProcessTracker::AssociateThreads(UniqueTid utid1, UniqueTid utid2) {
-  TraceStorage::Thread* thd1 = context_->storage->GetMutableThread(utid1);
-  TraceStorage::Thread* thd2 = context_->storage->GetMutableThread(utid2);
+  auto* tt = context_->storage->mutable_thread_table();
 
   // First of all check if one of the two threads is already bound to a process.
   // If that is the case, map the other thread to the same process and resolve
   // recursively any associations pending on the other thread.
 
-  if (thd1->upid.has_value() && !thd2->upid.has_value()) {
-    thd2->upid = *thd1->upid;
-    ResolvePendingAssociations(utid2, *thd1->upid);
+  auto opt_upid1 = tt->upid()[utid1];
+  auto opt_upid2 = tt->upid()[utid2];
+
+  if (opt_upid1.has_value() && !opt_upid2.has_value()) {
+    tt->mutable_upid()->Set(utid2, *opt_upid1);
+    ResolvePendingAssociations(utid2, *opt_upid1);
     return;
   }
 
-  if (thd2->upid.has_value() && !thd1->upid.has_value()) {
-    thd1->upid = *thd2->upid;
-    ResolvePendingAssociations(utid1, *thd2->upid);
+  if (opt_upid2.has_value() && !opt_upid1.has_value()) {
+    tt->mutable_upid()->Set(utid1, *opt_upid2);
+    ResolvePendingAssociations(utid1, *opt_upid2);
     return;
   }
 
-  if (thd1->upid.has_value() && thd1->upid != thd2->upid) {
+  if (opt_upid1.has_value() && opt_upid1 != opt_upid2) {
     // Cannot associate two threads that belong to two different processes.
     PERFETTO_ELOG("Process tracker failure. Cannot associate threads %u, %u",
-                  thd1->tid, thd2->tid);
+                  tt->tid()[utid1], tt->tid()[utid2]);
     context_->storage->IncrementStats(stats::process_tracker_errors);
     return;
   }
@@ -283,7 +337,10 @@
 
 void ProcessTracker::ResolvePendingAssociations(UniqueTid utid_arg,
                                                 UniquePid upid) {
-  PERFETTO_DCHECK(context_->storage->GetMutableThread(utid_arg)->upid == upid);
+  auto* tt = context_->storage->mutable_thread_table();
+  auto* pt = context_->storage->mutable_process_table();
+  PERFETTO_DCHECK(tt->upid()[utid_arg] == upid);
+
   std::vector<UniqueTid> resolved_utids;
   resolved_utids.emplace_back(utid_arg);
 
@@ -302,10 +359,9 @@
       PERFETTO_DCHECK(child_upid != upid);
 
       // Set the parent pid of the other process
-      auto* child_proc = context_->storage->GetMutableProcess(child_upid);
-      PERFETTO_DCHECK(!child_proc->parent_upid ||
-                      child_proc->parent_upid == upid);
-      child_proc->parent_upid = upid;
+      PERFETTO_DCHECK(!pt->parent_upid()[child_upid] ||
+                      pt->parent_upid()[child_upid] == upid);
+      pt->mutable_parent_upid()->Set(child_upid, upid);
 
       // Erase the pair. The |pending_parent_assocs_| vector is not sorted and
       // swapping a std::pair<uint32_t, uint32_t> is cheap.
@@ -327,9 +383,9 @@
       PERFETTO_DCHECK(other_utid != utid);
 
       // Update the other thread and associated it to the same process.
-      auto* other_thd = context_->storage->GetMutableThread(other_utid);
-      PERFETTO_DCHECK(!other_thd->upid || other_thd->upid == upid);
-      other_thd->upid = upid;
+      PERFETTO_DCHECK(!tt->upid()[other_utid] ||
+                      tt->upid()[other_utid] == upid);
+      tt->mutable_upid()->Set(other_utid, upid);
 
       // Erase the pair. The |pending_assocs_| vector is not sorted and swapping
       // a std::pair<uint32_t, uint32_t> is cheap.
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/process_tracker.h
index 19be682..d9141c4 100644
--- a/src/trace_processor/process_tracker.h
+++ b/src/trace_processor/process_tracker.h
@@ -48,7 +48,7 @@
 
   // Called when a task_newtask is observed. This force the tracker to start
   // a new UTID for the thread, which is needed for TID-recycling resolution.
-  UniqueTid StartNewThread(int64_t timestamp,
+  UniqueTid StartNewThread(base::Optional<int64_t> timestamp,
                            uint32_t tid,
                            StringId thread_name_id);
 
@@ -67,8 +67,8 @@
   // the thread_name_id.
   virtual UniqueTid UpdateThreadName(uint32_t tid, StringId thread_name_id);
 
-  // Assigns the given name to the thread identified |utid| if it does not have
-  // a name yet.
+  // Assigns the given name to the thread identified |utid| if it does not
+  // have a name yet.
   virtual void SetThreadNameIfUnset(UniqueTid utid, StringId thread_name_id);
 
   // Called when a thread is seen the process tree. Retrieves the matching utid
@@ -78,8 +78,8 @@
 
   // Called when a task_newtask without the CLONE_THREAD flag is observed.
   // This force the tracker to start both a new UTID and a new UPID.
-  UniquePid StartNewProcess(int64_t timestamp,
-                            uint32_t parent_tid,
+  UniquePid StartNewProcess(base::Optional<int64_t> timestamp,
+                            base::Optional<uint32_t> parent_tid,
                             uint32_t pid,
                             StringId main_thread_name);
 
@@ -95,7 +95,7 @@
 
   // Assigns the given name to the process identified by |upid| if it does not
   // have a name yet.
-  void SetProcessNameIfUnset(UniquePid upid, StringId process_name_id);
+  virtual void SetProcessNameIfUnset(UniquePid upid, StringId process_name_id);
 
   // Called on a task rename event to set the process name if the tid provided
   // is the main thread of the process.
@@ -126,14 +126,20 @@
   void AssociateThreads(UniqueTid, UniqueTid);
 
  private:
+  // Returns the utid of a thread having |tid| and |pid| as the parent process.
+  // pid == base::nullopt matches all processes.
+  // Returns base::nullopt if such a thread doesn't exist.
+  base::Optional<uint32_t> GetThreadOrNull(uint32_t tid,
+                                           base::Optional<uint32_t> pid);
+
+  // Returns whether a thread is considered alive by the process tracker.
+  bool IsThreadAlive(UniqueTid utid);
+
   // Called whenever we discover that the passed thread belongs to the passed
   // process. The |pending_assocs_| vector is scanned to see if there are any
   // other threads associated to the passed thread.
   void ResolvePendingAssociations(UniqueTid, UniquePid);
 
-  std::pair<UniquePid, TraceStorage::Process*> GetOrCreateProcessPtr(
-      uint32_t pid);
-
   TraceProcessorContext* const context_;
 
   // Each tid can have multiple UniqueTid entries, a new UniqueTid is assigned
diff --git a/src/trace_processor/process_tracker_unittest.cc b/src/trace_processor/process_tracker_unittest.cc
index 802ba42..e626681 100644
--- a/src/trace_processor/process_tracker_unittest.cc
+++ b/src/trace_processor/process_tracker_unittest.cc
@@ -34,6 +34,7 @@
  public:
   ProcessTrackerTest() {
     context.storage.reset(new TraceStorage());
+    context.global_args_tracker.reset(new GlobalArgsTracker(&context));
     context.args_tracker.reset(new ArgsTracker(&context));
     context.process_tracker.reset(new ProcessTracker(&context));
     context.event_tracker.reset(new EventTracker(&context));
@@ -58,9 +59,10 @@
 
 TEST_F(ProcessTrackerTest, StartNewProcess) {
   TraceStorage storage;
-  auto upid = context.process_tracker->StartNewProcess(1000, 0, 123, 0);
+  auto upid =
+      context.process_tracker->StartNewProcess(1000, 0u, 123, kNullStringId);
   ASSERT_EQ(context.process_tracker->GetOrCreateProcess(123), upid);
-  ASSERT_EQ(context.storage->GetProcess(upid).start_ns, 1000);
+  ASSERT_EQ(context.storage->process_table().start_ts()[upid], 1000);
 }
 
 TEST_F(ProcessTrackerTest, PushTwoProcessEntries_SamePidAndName) {
@@ -82,8 +84,9 @@
 
 TEST_F(ProcessTrackerTest, AddProcessEntry_CorrectName) {
   context.process_tracker->SetProcessMetadata(1, base::nullopt, "test");
-  ASSERT_EQ(context.storage->GetString(context.storage->GetProcess(1).name_id),
-            "test");
+  auto name =
+      context.storage->GetString(context.storage->process_table().name()[1]);
+  ASSERT_EQ(name, "test");
 }
 
 TEST_F(ProcessTrackerTest, UpdateThreadMatch) {
@@ -105,29 +108,42 @@
   context.process_tracker->SetProcessMetadata(2, base::nullopt, "test");
   context.process_tracker->UpdateThread(4, 2);
 
-  TraceStorage::Thread thread = context.storage->GetThread(/*utid=*/1);
-  TraceStorage::Process process = context.storage->GetProcess(/*utid=*/1);
-
-  ASSERT_EQ(thread.tid, 4u);
-  ASSERT_EQ(thread.upid.value(), 1u);
-  ASSERT_EQ(process.pid, 2u);
-  ASSERT_EQ(process.start_ns, 0);
+  ASSERT_EQ(context.storage->thread_table().tid()[1], 4u);
+  ASSERT_EQ(context.storage->thread_table().upid()[1].value(), 1u);
+  ASSERT_EQ(context.storage->process_table().pid()[1], 2u);
+  ASSERT_EQ(context.storage->process_table().start_ts()[1], base::nullopt);
 }
 
 TEST_F(ProcessTrackerTest, UpdateThreadCreate) {
   context.process_tracker->UpdateThread(12, 2);
 
-  TraceStorage::Thread thread = context.storage->GetThread(1);
-
   // We expect 3 threads: Invalid thread, main thread for pid, tid 12.
-  ASSERT_EQ(context.storage->thread_count(), 3u);
+  ASSERT_EQ(context.storage->thread_table().row_count(), 3u);
 
   auto tid_it = context.process_tracker->UtidsForTidForTesting(12);
   ASSERT_NE(tid_it.first, tid_it.second);
-  ASSERT_EQ(thread.upid.value(), 1u);
+  ASSERT_EQ(context.storage->thread_table().upid()[1].value(), 1u);
   auto pid_it = context.process_tracker->UpidsForPidForTesting(2);
   ASSERT_NE(pid_it.first, pid_it.second);
-  ASSERT_EQ(context.storage->process_count(), 2u);
+  ASSERT_EQ(context.storage->process_table().row_count(), 2u);
+}
+
+TEST_F(ProcessTrackerTest, PidReuseWithoutStartAndEndThread) {
+  UniquePid p1 = context.process_tracker->StartNewProcess(
+      base::nullopt, base::nullopt, /*pid=*/1, kNullStringId);
+  UniqueTid t1 = context.process_tracker->UpdateThread(/*tid=*/2, /*pid=*/1);
+
+  UniquePid p2 = context.process_tracker->StartNewProcess(
+      base::nullopt, base::nullopt, /*pid=*/1, kNullStringId);
+  UniqueTid t2 = context.process_tracker->UpdateThread(/*tid=*/2, /*pid=*/1);
+
+  ASSERT_NE(p1, p2);
+  ASSERT_NE(t1, t2);
+
+  // We expect 3 processes: idle process, 2x pid 1.
+  ASSERT_EQ(context.storage->process_table().row_count(), 3u);
+  // We expect 5 threads: Invalid thread, 2x (main thread + sub thread).
+  ASSERT_EQ(context.storage->thread_table().row_count(), 5u);
 }
 
 }  // namespace
diff --git a/src/trace_processor/proto_to_json.cc b/src/trace_processor/proto_to_json.cc
index 5b07889..e6f22e9 100644
--- a/src/trace_processor/proto_to_json.cc
+++ b/src/trace_processor/proto_to_json.cc
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
+#include <vector>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/message.h>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/proto_to_json.h"
 
 namespace perfetto {
@@ -160,14 +165,162 @@
   return ret;
 }
 
+// This is a class helps avoid the situation where every function has to take
+// field_options_prototype as an argument, which becomes distracting.
+class OptionsConverter {
+ public:
+  explicit OptionsConverter(
+      const google::protobuf::Message* field_options_prototype)
+      : field_options_prototype_(field_options_prototype) {}
+
+  // Prints all field options for non-empty fields of a message. Example:
+  // --- Message definitions ---
+  // FooMessage {
+  //   repeated int64 foo = 1 [op1 = val1, op2 = val2];
+  //   optional BarMessage bar = 2 [op3 = val3];
+  // }
+  //
+  // BarMessage {
+  //   optional int64 baz = 1 [op4 = val4];
+  // }
+  // --- MessageInstance ---
+  // foo_msg = {  // (As JSON)
+  //   foo: [23, 24, 25],
+  //   bar: {
+  //     baz: 42
+  //   }
+  // }
+  // --- Output of MessageFieldOptionsToJson(foo_msg) ---
+  //   foo: {
+  //     __field_options: {
+  //       op1: val1,
+  //       op2: val2,
+  //     },
+  //     __repeated: true
+  //   }
+  //   bar: {
+  //     __field_options: {
+  //       op3 = val3,
+  //     },
+  //     baz: {
+  //       __field_options: {
+  //         op4 = val4
+  //       },
+  //     }
+  //   }
+  // --- Notes ---
+  // This function does not produce the surrounding braces for easier use in
+  // recursive use cases. The caller needs to surround the output with braces.
+  std::string MessageFieldOptionsToJson(
+      const google::protobuf::Message& message,
+      uint32_t indent) {
+    using google::protobuf::FieldDescriptor;
+    std::vector<const FieldDescriptor*> field_descs;
+    message.GetReflection()->ListFields(message, &field_descs);
+    std::vector<std::string> field_outputs;
+    for (auto* field_desc : field_descs) {
+      std::vector<std::string> field_entries;
+      if (HasFieldOptions(field_desc)) {
+        std::string options_entry;
+        options_entry +=
+            std::string(indent + 2, ' ') + R"("__field_options": )";
+        options_entry += FieldOptionsToJson(field_desc, indent + 4);
+        field_entries.push_back(std::move(options_entry));
+      }
+      std::string nested_fields =
+          NestedMessageFieldOptionsToJson(message, field_desc, indent + 2);
+      if (nested_fields != "") {
+        field_entries.push_back(std::move(nested_fields));
+      }
+      // We don't output annotations for a field if that field and all its
+      // descendants have no field options.
+      if (field_entries.size() > 0) {
+        if (field_desc->is_repeated()) {
+          field_entries.push_back(std::string(indent, ' ') +
+                                  R"("__repeated": true)");
+        }
+        std::string field_output;
+        const std::string& name = field_desc->is_extension()
+                                      ? field_desc->full_name()
+                                      : field_desc->name();
+        field_output += std::string(indent, ' ') + "\"" + name + "\": {\n";
+        field_output += base::Join(field_entries, ",\n") + "\n";
+        field_output += std::string(indent, ' ') + "}";
+        field_outputs.push_back(std::move(field_output));
+      }
+    }
+    return base::Join(field_outputs, ",\n");
+  }
+
+ private:
+  static bool HasFieldOptions(
+      const google::protobuf::FieldDescriptor* field_desc) {
+    return field_desc->options().ByteSizeLong() > 0;
+  }
+
+  std::string NestedMessageFieldOptionsToJson(
+      const google::protobuf::Message& message,
+      const google::protobuf::FieldDescriptor* field_desc,
+      uint32_t indent) {
+    using google::protobuf::FieldDescriptor;
+    if (field_desc->cpp_type() != FieldDescriptor::CppType::CPPTYPE_MESSAGE)
+      return "";
+    const auto* reflection = message.GetReflection();
+    const google::protobuf::Message& nested_message =
+        field_desc->is_repeated()
+            ? reflection->GetRepeatedMessage(message, field_desc, 0)
+            : reflection->GetMessage(message, field_desc);
+    return MessageFieldOptionsToJson(nested_message, indent);
+  }
+
+  std::string FieldOptionsToJson(
+      const google::protobuf::FieldDescriptor* field_desc,
+      uint32_t indent) {
+    PERFETTO_DCHECK(HasFieldOptions(field_desc));
+    std::unique_ptr<google::protobuf::Message> options(
+        field_options_prototype_->New());
+    // Field option extensions are compiled at runtime as opposed to being
+    // compiled in and being part of the generated pool, so the field option
+    // must be re-parsed as a dynamic message for the extensions to show up. If
+    // we do not do this, the extension fields remain "unknown fields" to the
+    // reflection API.
+    options->ParseFromString(field_desc->options().SerializeAsString());
+    return MessageToJson(*options, indent);
+  }
+
+  const google::protobuf::Message* field_options_prototype_;
+};
+
 }  // namespace
 
 std::string MessageToJson(const google::protobuf::Message& message,
                           uint32_t indent) {
-  return "{" + MessageFieldsToJson(message, indent + 2) + "\n" +
+  return "{" + MessageFieldsToJson(message, indent + 2) + '\n' +
          std::string(indent, ' ') + "}";
 }
 
+std::string MessageToJsonWithAnnotations(
+    const google::protobuf::Message& message,
+    const google::protobuf::Message* field_options_prototype,
+    uint32_t indent) {
+  std::string ret;
+  OptionsConverter options_converter(field_options_prototype);
+
+  ret = "{" + MessageFieldsToJson(message, indent + 2);
+  std::string annotation_fields =
+      options_converter.MessageFieldOptionsToJson(message, indent + 4);
+  if (annotation_fields != "") {
+    ret += ",\n";
+    ret += std::string(indent + 2, ' ') + "\"__annotations\": {\n";
+    ret += annotation_fields + "\n";
+    ret += std::string(indent + 2, ' ') + "}\n";
+  } else {
+    ret += "\n";
+  }
+  ret += std::string(indent, ' ') + "}\n";
+  return ret;
+}
+
 }  // namespace proto_to_json
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/proto_to_json.h b/src/trace_processor/proto_to_json.h
index f46f4c1..90cae5c 100644
--- a/src/trace_processor/proto_to_json.h
+++ b/src/trace_processor/proto_to_json.h
@@ -26,6 +26,11 @@
 std::string MessageToJson(const google::protobuf::Message& message,
                           uint32_t indent = 0);
 
+std::string MessageToJsonWithAnnotations(
+    const google::protobuf::Message& message,
+    const google::protobuf::Message* field_options_prototype,
+    uint32_t indent = 0);
+
 }  // namespace proto_to_json
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/rpc/BUILD.gn b/src/trace_processor/rpc/BUILD.gn
index 02ef12f..8ca56b1 100644
--- a/src/trace_processor/rpc/BUILD.gn
+++ b/src/trace_processor/rpc/BUILD.gn
@@ -54,9 +54,7 @@
 
 if (enable_perfetto_ui && is_wasm) {
   source_set("wasm_bridge") {
-    sources = [
-      "wasm_bridge.cc",
-    ]
+    sources = [ "wasm_bridge.cc" ]
     deps = [
       ":rpc",
       "../../../gn:default_deps",
diff --git a/src/trace_processor/sched_slice_table.cc b/src/trace_processor/sched_slice_table.cc
index 67694d9..49d4a0a 100644
--- a/src/trace_processor/sched_slice_table.cc
+++ b/src/trace_processor/sched_slice_table.cc
@@ -83,7 +83,7 @@
     // it's actually better to do subqueries on this table. Estimate the cost
     // of filtering on utid equality constraint by dividing the number of slices
     // by the number of threads.
-    return RowCount() / storage_->thread_count();
+    return RowCount() / storage_->thread_table().row_count();
   }
 
   // If we get to this point, we do not have any special filter logic so
diff --git a/src/trace_processor/sched_slice_table_unittest.cc b/src/trace_processor/sched_slice_table_unittest.cc
index f9131ae..7e624d0 100644
--- a/src/trace_processor/sched_slice_table_unittest.cc
+++ b/src/trace_processor/sched_slice_table_unittest.cc
@@ -41,6 +41,7 @@
     db_.reset(db);
 
     context_.storage.reset(new TraceStorage());
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.process_tracker.reset(new ProcessTracker(&context_));
     context_.event_tracker.reset(new EventTracker(&context_));
diff --git a/src/trace_processor/slice_tracker.cc b/src/trace_processor/slice_tracker.cc
index 290c8d5..39f7d96 100644
--- a/src/trace_processor/slice_tracker.cc
+++ b/src/trace_processor/slice_tracker.cc
@@ -42,16 +42,22 @@
                                              StringId category,
                                              StringId name,
                                              SetArgsCallback args_callback) {
-  // At this stage all events should be globally timestamp ordered.
-  if (timestamp < prev_timestamp_) {
-    context_->storage->IncrementStats(stats::slice_out_of_order);
-    return base::nullopt;
-  }
-  prev_timestamp_ = timestamp;
+  tables::SliceTable::Row row(timestamp, kPendingDuration, track_id.value,
+                              category, name);
+  return StartSlice(timestamp, track_id, args_callback, [this, &row]() {
+    return context_->storage->mutable_slice_table()->Insert(row);
+  });
+}
 
-  MaybeCloseStack(timestamp, &stacks_[track_id]);
-  return StartSlice(timestamp, kPendingDuration, track_id, category, name,
-                    args_callback);
+void SliceTracker::BeginGpu(tables::GpuSliceTable::Row row,
+                            SetArgsCallback args_callback) {
+  // Ensure that the duration is pending for this row.
+  // TODO(lalitm): change this to eventually use null instead of -1.
+  row.dur = kPendingDuration;
+
+  StartSlice(row.ts, TrackId(row.track_id), args_callback, [this, &row]() {
+    return context_->storage->mutable_gpu_slice_table()->Insert(row);
+  });
 }
 
 base::Optional<uint32_t> SliceTracker::Scoped(int64_t timestamp,
@@ -60,6 +66,51 @@
                                               StringId name,
                                               int64_t duration,
                                               SetArgsCallback args_callback) {
+  PERFETTO_DCHECK(duration >= 0);
+
+  tables::SliceTable::Row row(timestamp, duration, track_id.value, category,
+                              name);
+  return StartSlice(timestamp, track_id, args_callback, [this, &row]() {
+    return context_->storage->mutable_slice_table()->Insert(row);
+  });
+}
+
+void SliceTracker::ScopedGpu(const tables::GpuSliceTable::Row& row,
+                             SetArgsCallback args_callback) {
+  PERFETTO_DCHECK(row.dur >= 0);
+
+  StartSlice(row.ts, TrackId(row.track_id), args_callback, [this, &row]() {
+    return context_->storage->mutable_gpu_slice_table()->Insert(row);
+  });
+}
+
+base::Optional<uint32_t> SliceTracker::End(int64_t timestamp,
+                                           TrackId track_id,
+                                           StringId category,
+                                           StringId name,
+                                           SetArgsCallback args_callback) {
+  auto finder = [this, category, name](const SlicesStack& stack) {
+    return MatchingIncompleteSliceIndex(stack, name, category);
+  };
+  auto slice_id = CompleteSlice(timestamp, track_id, args_callback, finder);
+  if (!slice_id)
+    return base::nullopt;
+  return context_->storage->slice_table().id().IndexOf(*slice_id);
+}
+
+base::Optional<SliceId> SliceTracker::EndGpu(int64_t ts,
+                                             TrackId t_id,
+                                             SetArgsCallback args_callback) {
+  return CompleteSlice(ts, t_id, args_callback, [](const SlicesStack& stack) {
+    return static_cast<uint32_t>(stack.size() - 1);
+  });
+}
+
+base::Optional<uint32_t> SliceTracker::StartSlice(
+    int64_t timestamp,
+    TrackId track_id,
+    SetArgsCallback args_callback,
+    std::function<SliceId()> inserter) {
   // At this stage all events should be globally timestamp ordered.
   if (timestamp < prev_timestamp_) {
     context_->storage->IncrementStats(stats::slice_out_of_order);
@@ -67,21 +118,9 @@
   }
   prev_timestamp_ = timestamp;
 
-  PERFETTO_DCHECK(duration >= 0);
-  MaybeCloseStack(timestamp, &stacks_[track_id]);
-  return StartSlice(timestamp, duration, track_id, category, name,
-                    args_callback);
-}
-
-base::Optional<uint32_t> SliceTracker::StartSlice(
-    int64_t timestamp,
-    int64_t duration,
-    TrackId track_id,
-    StringId category,
-    StringId name,
-    SetArgsCallback args_callback) {
   auto* stack = &stacks_[track_id];
   auto* slices = context_->storage->mutable_slice_table();
+  MaybeCloseStack(timestamp, stack);
 
   const uint8_t depth = static_cast<uint8_t>(stack->size());
   if (depth >= std::numeric_limits<uint8_t>::max()) {
@@ -91,27 +130,70 @@
   int64_t parent_stack_id =
       depth == 0 ? 0 : slices->stack_id()[stack->back().first];
 
-  tables::SliceTable::Row row(timestamp, duration, track_id.value, category,
-                              name, depth, 0, parent_stack_id);
-  auto id = slices->Insert(row);
+  SliceId id = inserter();
   uint32_t slice_idx = *slices->id().IndexOf(id);
   stack->emplace_back(std::make_pair(slice_idx, ArgsTracker(context_)));
 
+  // Post fill all the relevant columns. All the other columns should have
+  // been filled by the inserter.
+  slices->mutable_depth()->Set(slice_idx, depth);
+  slices->mutable_parent_stack_id()->Set(slice_idx, parent_stack_id);
+  slices->mutable_stack_id()->Set(slice_idx, GetStackHash(*stack));
+
   if (args_callback) {
     ArgsTracker* tracker = &stack->back().second;
-    ArgsTracker::BoundInserter inserter(tracker, TableId::kNestableSlices,
-                                        slice_idx);
-    args_callback(&inserter);
+    auto bound_inserter = tracker->AddArgsTo(id);
+    args_callback(&bound_inserter);
   }
-  slices->mutable_stack_id()->Set(slice_idx, GetStackHash(*stack));
   return slice_idx;
 }
 
+base::Optional<SliceId> SliceTracker::CompleteSlice(
+    int64_t timestamp,
+    TrackId track_id,
+    SetArgsCallback args_callback,
+    std::function<base::Optional<uint32_t>(const SlicesStack&)> finder) {
+  // At this stage all events should be globally timestamp ordered.
+  if (timestamp < prev_timestamp_) {
+    context_->storage->IncrementStats(stats::slice_out_of_order);
+    return base::nullopt;
+  }
+  prev_timestamp_ = timestamp;
+
+  auto& stack = stacks_[track_id];
+  MaybeCloseStack(timestamp, &stack);
+  if (stack.empty())
+    return base::nullopt;
+
+  auto* slices = context_->storage->mutable_slice_table();
+  base::Optional<uint32_t> stack_idx = finder(stack);
+
+  // If we are trying to close slices that are not open on the stack (e.g.,
+  // slices that began before tracing started), bail out.
+  if (!stack_idx)
+    return base::nullopt;
+
+  uint32_t slice_idx = stack[stack_idx.value()].first;
+  PERFETTO_DCHECK(slices->dur()[slice_idx] == kPendingDuration);
+  slices->mutable_dur()->Set(slice_idx, timestamp - slices->ts()[slice_idx]);
+
+  if (args_callback) {
+    ArgsTracker* tracker = &stack.back().second;
+    auto bound_inserter = tracker->AddArgsTo(slices->id()[slice_idx]);
+    args_callback(&bound_inserter);
+  }
+
+  // If this slice is the top slice on the stack, pop it off.
+  if (*stack_idx == stack.size() - 1)
+    stack.pop_back();
+  return slices->id()[slice_idx];
+}
+
 // Returns the first incomplete slice in the stack with matching name and
 // category. We assume null category/name matches everything. Returns
 // nullopt if no matching slice is found.
-base::Optional<size_t> SliceTracker::MatchingIncompleteSliceIndex(
-    SlicesStack& stack,
+base::Optional<uint32_t> SliceTracker::MatchingIncompleteSliceIndex(
+    const SlicesStack& stack,
     StringId name,
     StringId category) {
   auto* slices = context_->storage->mutable_slice_table();
@@ -126,68 +208,11 @@
     const StringId& other_name = slices->name()[slice_idx];
     if (!name.is_null() && !other_name.is_null() && name != other_name)
       continue;
-    return static_cast<size_t>(i);
+    return static_cast<uint32_t>(i);
   }
   return base::nullopt;
 }
 
-base::Optional<uint32_t> SliceTracker::End(int64_t timestamp,
-                                           TrackId track_id,
-                                           StringId category,
-                                           StringId name,
-                                           SetArgsCallback args_callback) {
-  // At this stage all events should be globally timestamp ordered.
-  if (timestamp < prev_timestamp_) {
-    context_->storage->IncrementStats(stats::slice_out_of_order);
-    return base::nullopt;
-  }
-  prev_timestamp_ = timestamp;
-
-  MaybeCloseStack(timestamp, &stacks_[track_id]);
-
-  auto& stack = stacks_[track_id];
-  if (stack.empty())
-    return base::nullopt;
-
-  auto* slices = context_->storage->mutable_slice_table();
-  base::Optional<size_t> stack_idx =
-      MatchingIncompleteSliceIndex(stack, name, category);
-
-  // If we are trying to close slices that are not open on the stack (e.g.,
-  // slices that began before tracing started), bail out.
-  if (!stack_idx)
-    return base::nullopt;
-
-  if (*stack_idx != stack.size() - 1) {
-    // This usually happens because we have two slices that are partially
-    // overlapping.
-    // [  slice  1    ]
-    //          [     slice 2     ]
-    // This is invalid in chrome and should be fixed. Duration events should
-    // either be nested or disjoint, never partially intersecting.
-    PERFETTO_DLOG(
-        "Incorrect ordering of End slice event around timestamp "
-        "%" PRId64,
-        timestamp);
-    context_->storage->IncrementStats(stats::misplaced_end_event);
-  }
-
-  uint32_t slice_idx = stack[stack_idx.value()].first;
-
-  PERFETTO_DCHECK(slices->dur()[slice_idx] == kPendingDuration);
-  slices->mutable_dur()->Set(slice_idx, timestamp - slices->ts()[slice_idx]);
-
-  if (args_callback) {
-    ArgsTracker* tracker = &stack.back().second;
-    ArgsTracker::BoundInserter inserter(tracker, TableId::kNestableSlices,
-                                        slice_idx);
-    args_callback(&inserter);
-  }
-
-  return CompleteSlice(track_id);
-  // TODO(primiano): auto-close B slices left open at the end.
-}
-
 void SliceTracker::FlushPendingSlices() {
   // Clear the remaining stack entries. This ensures that any pending args are
   // written to the storage. We don't close any slices with kPendingDuration so
@@ -199,40 +224,53 @@
   stacks_.clear();
 }
 
-base::Optional<uint32_t> SliceTracker::CompleteSlice(TrackId track_id) {
-  auto& stack = stacks_[track_id];
-  uint32_t slice_idx = stack.back().first;
-  stack.pop_back();
-  return slice_idx;
-}
-
 void SliceTracker::MaybeCloseStack(int64_t ts, SlicesStack* stack) {
-  const auto& slices = context_->storage->slice_table();
-  bool pending_dur_descendent = false;
+  auto* slices = context_->storage->mutable_slice_table();
+  bool incomplete_descendent = false;
   for (int i = static_cast<int>(stack->size()) - 1; i >= 0; i--) {
     uint32_t slice_idx = (*stack)[static_cast<size_t>(i)].first;
 
-    int64_t start_ts = slices.ts()[slice_idx];
-    int64_t dur = slices.dur()[slice_idx];
+    int64_t start_ts = slices->ts()[slice_idx];
+    int64_t dur = slices->dur()[slice_idx];
     int64_t end_ts = start_ts + dur;
     if (dur == kPendingDuration) {
-      pending_dur_descendent = true;
+      incomplete_descendent = true;
+      continue;
     }
 
-    if (pending_dur_descendent) {
+    if (incomplete_descendent) {
       PERFETTO_DCHECK(ts >= start_ts);
-      // Some trace producers emit END events in the wrong order (even after
-      // sorting by timestamp), e.g. BEGIN A, BEGIN B, END A, END B. We discard
-      // the mismatching END A in End(). Because of this, we can end up in a
-      // situation where we attempt to close the stack on top of A at a
-      // timestamp beyond A's parent. To avoid crashing in such a case, we just
-      // emit a warning instead.
-      if (dur != kPendingDuration && ts > end_ts) {
-        PERFETTO_DLOG(
-            "Incorrect ordering of begin/end slice events around timestamp "
-            "%" PRId64,
-            ts);
+
+      // Only process slices if the ts is past the end of the slice.
+      if (ts <= end_ts)
+        continue;
+
+      // This usually happens because we have two slices that are partially
+      // overlapping.
+      // [  slice  1    ]
+      //          [     slice 2     ]
+      // This is invalid in chrome and should be fixed. Duration events should
+      // either be nested or disjoint, never partially intersecting.
+      PERFETTO_DLOG(
+          "Incorrect ordering of begin/end slice events around timestamp "
+          "%" PRId64,
+          ts);
+      context_->storage->IncrementStats(stats::misplaced_end_event);
+
+      // Every slice below this one should have a pending duration. Update
+      // of them to have the end ts of the current slice and pop them
+      // all off.
+      for (int j = static_cast<int>(stack->size()) - 1; j > i; --j) {
+        uint32_t child_idx = (*stack)[static_cast<size_t>(j)].first;
+        PERFETTO_DCHECK(slices->dur()[child_idx] == kPendingDuration);
+        slices->mutable_dur()->Set(child_idx, end_ts - slices->ts()[child_idx]);
+        stack->pop_back();
       }
+
+      // Also pop the current row itself and reset the incomplete flag.
+      stack->pop_back();
+      incomplete_descendent = false;
+
       continue;
     }
 
@@ -253,7 +291,14 @@
     hash.Update(slices.category()[slice_idx]);
     hash.Update(slices.name()[slice_idx]);
   }
-  return static_cast<int64_t>(hash.digest());
+
+  // For clients which don't have an integer type (i.e. Javascript), returning
+  // hashes which have the top 11 bits set leads to numbers which are
+  // unrepresenatble. This means that clients cannot filter using this number as
+  // it will be meaningless when passed back to us. For this reason, make sure
+  // that the hash is always less than 2^53 - 1.
+  constexpr uint64_t kSafeBitmask = (1ull << 53) - 1;
+  return static_cast<int64_t>(hash.digest() & kSafeBitmask);
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/slice_tracker.h b/src/trace_processor/slice_tracker.h
index a2ffd86..36fe5ef 100644
--- a/src/trace_processor/slice_tracker.h
+++ b/src/trace_processor/slice_tracker.h
@@ -43,6 +43,9 @@
       StringId name,
       SetArgsCallback args_callback = SetArgsCallback());
 
+  void BeginGpu(tables::GpuSliceTable::Row row,
+                SetArgsCallback args_callback = SetArgsCallback());
+
   // virtual for testing
   virtual base::Optional<uint32_t> Scoped(
       int64_t timestamp,
@@ -52,6 +55,9 @@
       int64_t duration,
       SetArgsCallback args_callback = SetArgsCallback());
 
+  void ScopedGpu(const tables::GpuSliceTable::Row& row,
+                 SetArgsCallback args_callback = SetArgsCallback());
+
   // virtual for testing
   virtual base::Optional<uint32_t> End(
       int64_t timestamp,
@@ -60,6 +66,13 @@
       StringId opt_name = {},
       SetArgsCallback args_callback = SetArgsCallback());
 
+  // TODO(lalitm): eventually this method should become End and End should
+  // be renamed EndChrome.
+  base::Optional<SliceId> EndGpu(
+      int64_t ts,
+      TrackId track_id,
+      SetArgsCallback args_callback = SetArgsCallback());
+
   void FlushPendingSlices();
 
  private:
@@ -67,17 +80,22 @@
   using StackMap = std::unordered_map<TrackId, SlicesStack>;
 
   base::Optional<uint32_t> StartSlice(int64_t timestamp,
-                                      int64_t duration,
                                       TrackId track_id,
-                                      StringId category,
-                                      StringId name,
-                                      SetArgsCallback args_callback);
-  base::Optional<uint32_t> CompleteSlice(TrackId track_id);
+                                      SetArgsCallback args_callback,
+                                      std::function<SliceId()> inserter);
+
+  base::Optional<SliceId> CompleteSlice(
+      int64_t timestamp,
+      TrackId track_id,
+      SetArgsCallback args_callback,
+      std::function<base::Optional<uint32_t>(const SlicesStack&)> finder);
 
   void MaybeCloseStack(int64_t end_ts, SlicesStack*);
-  base::Optional<size_t> MatchingIncompleteSliceIndex(SlicesStack& stack,
-                                                      StringId name,
-                                                      StringId category);
+
+  base::Optional<uint32_t> MatchingIncompleteSliceIndex(
+      const SlicesStack& stack,
+      StringId name,
+      StringId category);
   int64_t GetStackHash(const SlicesStack&);
 
   // Timestamp of the previous event. Used to discard events arriving out
diff --git a/src/trace_processor/slice_tracker_unittest.cc b/src/trace_processor/slice_tracker_unittest.cc
index a38709e..6b27258 100644
--- a/src/trace_processor/slice_tracker_unittest.cc
+++ b/src/trace_processor/slice_tracker_unittest.cc
@@ -56,16 +56,18 @@
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
-  tracker.Begin(2 /*ts*/, track, 0 /*cat*/, 1 /*name*/);
-  tracker.End(10 /*ts*/, track, 0 /*cat*/, 1 /*name*/);
+  tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/,
+                StringId::Raw(1) /*name*/);
+  tracker.End(10 /*ts*/, track, kNullStringId /*cat*/,
+              StringId::Raw(1) /*name*/);
 
   const auto& slices = context.storage->slice_table();
   EXPECT_EQ(slices.row_count(), 1u);
   EXPECT_EQ(slices.ts()[0], 2);
   EXPECT_EQ(slices.dur()[0], 8);
   EXPECT_EQ(slices.track_id()[0], track.value);
-  EXPECT_EQ(slices.category()[0], 0u);
-  EXPECT_EQ(slices.name()[0], 1u);
+  EXPECT_EQ(slices.category()[0].raw_id(), 0u);
+  EXPECT_EQ(slices.name()[0].raw_id(), 1u);
   EXPECT_EQ(slices.depth()[0], 0u);
   EXPECT_EQ(slices.arg_set_id()[0], kInvalidArgSetId);
 }
@@ -73,17 +75,22 @@
 TEST(SliceTrackerTest, OneSliceWithArgs) {
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
+  context.global_args_tracker.reset(new GlobalArgsTracker(&context));
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
-  tracker.Begin(2 /*ts*/, track, 0 /*cat*/, 1 /*name*/,
+  tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/,
+                StringId::Raw(1) /*name*/,
                 [](ArgsTracker::BoundInserter* inserter) {
-                  inserter->AddArg(/*flat_key=*/1, /*key=*/2,
+                  inserter->AddArg(/*flat_key=*/StringId::Raw(1),
+                                   /*key=*/StringId::Raw(2),
                                    /*value=*/Variadic::Integer(10));
                 });
-  tracker.End(10 /*ts*/, track, 0 /*cat*/, 1 /*name*/,
+  tracker.End(10 /*ts*/, track, kNullStringId /*cat*/,
+              StringId::Raw(1) /*name*/,
               [](ArgsTracker::BoundInserter* inserter) {
-                inserter->AddArg(/*flat_key=*/3, /*key=*/4,
+                inserter->AddArg(/*flat_key=*/StringId::Raw(3),
+                                 /*key=*/StringId::Raw(4),
                                  /*value=*/Variadic::Integer(20));
               });
 
@@ -92,20 +99,20 @@
   EXPECT_EQ(slices.ts()[0], 2);
   EXPECT_EQ(slices.dur()[0], 8);
   EXPECT_EQ(slices.track_id()[0], track.value);
-  EXPECT_EQ(slices.category()[0], 0u);
-  EXPECT_EQ(slices.name()[0], 1u);
+  EXPECT_EQ(slices.category()[0].raw_id(), 0u);
+  EXPECT_EQ(slices.name()[0].raw_id(), 1u);
   EXPECT_EQ(slices.depth()[0], 0u);
   auto set_id = slices.arg_set_id()[0];
 
-  auto args = context.storage->args();
-  EXPECT_EQ(args.set_ids()[0], set_id);
-  EXPECT_EQ(args.flat_keys()[0], 1u);
-  EXPECT_EQ(args.keys()[0], 2u);
-  EXPECT_EQ(args.arg_values()[0], Variadic::Integer(10));
-  EXPECT_EQ(args.set_ids()[1], set_id);
-  EXPECT_EQ(args.flat_keys()[1], 3u);
-  EXPECT_EQ(args.keys()[1], 4u);
-  EXPECT_EQ(args.arg_values()[1], Variadic::Integer(20));
+  const auto& args = context.storage->arg_table();
+  EXPECT_EQ(args.arg_set_id()[0], set_id);
+  EXPECT_EQ(args.flat_key()[0].raw_id(), 1u);
+  EXPECT_EQ(args.key()[0].raw_id(), 2u);
+  EXPECT_EQ(args.int_value()[0], 10);
+  EXPECT_EQ(args.arg_set_id()[1], set_id);
+  EXPECT_EQ(args.flat_key()[1].raw_id(), 3u);
+  EXPECT_EQ(args.key()[1].raw_id(), 4u);
+  EXPECT_EQ(args.int_value()[1], 20);
 }
 
 TEST(SliceTrackerTest, TwoSliceDetailed) {
@@ -114,8 +121,10 @@
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
-  tracker.Begin(2 /*ts*/, track, 0 /*cat*/, 1 /*name*/);
-  tracker.Begin(3 /*ts*/, track, 0 /*cat*/, 2 /*name*/);
+  tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/,
+                StringId::Raw(1) /*name*/);
+  tracker.Begin(3 /*ts*/, track, kNullStringId /*cat*/,
+                StringId::Raw(2) /*name*/);
   tracker.End(5 /*ts*/, track);
   tracker.End(10 /*ts*/, track);
 
@@ -127,15 +136,15 @@
   EXPECT_EQ(slices.ts()[idx], 2);
   EXPECT_EQ(slices.dur()[idx], 8);
   EXPECT_EQ(slices.track_id()[idx], track.value);
-  EXPECT_EQ(slices.category()[idx], 0u);
-  EXPECT_EQ(slices.name()[idx], 1u);
+  EXPECT_EQ(slices.category()[idx].raw_id(), 0u);
+  EXPECT_EQ(slices.name()[idx].raw_id(), 1u);
   EXPECT_EQ(slices.depth()[idx++], 0u);
 
   EXPECT_EQ(slices.ts()[idx], 3);
   EXPECT_EQ(slices.dur()[idx], 2);
   EXPECT_EQ(slices.track_id()[idx], track.value);
-  EXPECT_EQ(slices.category()[idx], 0u);
-  EXPECT_EQ(slices.name()[idx], 2u);
+  EXPECT_EQ(slices.category()[idx].raw_id(), 0u);
+  EXPECT_EQ(slices.name()[idx].raw_id(), 2u);
   EXPECT_EQ(slices.depth()[idx], 1u);
 
   EXPECT_EQ(slices.parent_stack_id()[0], 0);
@@ -149,9 +158,9 @@
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
-  tracker.Begin(0 /*ts*/, track, 0, 0);
-  tracker.Begin(1 /*ts*/, track, 0, 0);
-  tracker.Scoped(2 /*ts*/, track, 0, 0, 6);
+  tracker.Begin(0 /*ts*/, track, kNullStringId, kNullStringId);
+  tracker.Begin(1 /*ts*/, track, kNullStringId, kNullStringId);
+  tracker.Scoped(2 /*ts*/, track, kNullStringId, kNullStringId, 6);
   tracker.End(9 /*ts*/, track);
   tracker.End(10 /*ts*/, track);
 
@@ -166,10 +175,14 @@
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
-  tracker.Begin(2 /*ts*/, track, 5 /*cat*/, 1 /*name*/);
-  tracker.End(3 /*ts*/, track, 1 /*cat*/, 1 /*name*/);
-  tracker.End(4 /*ts*/, track, 0 /*cat*/, 2 /*name*/);
-  tracker.End(5 /*ts*/, track, 5 /*cat*/, 1 /*name*/);
+  tracker.Begin(2 /*ts*/, track, StringId::Raw(5) /*cat*/,
+                StringId::Raw(1) /*name*/);
+  tracker.End(3 /*ts*/, track, StringId::Raw(1) /*cat*/,
+              StringId::Raw(1) /*name*/);
+  tracker.End(4 /*ts*/, track, kNullStringId /*cat*/,
+              StringId::Raw(2) /*name*/);
+  tracker.End(5 /*ts*/, track, StringId::Raw(5) /*cat*/,
+              StringId::Raw(1) /*name*/);
 
   auto slices = ToSliceInfo(context.storage->slice_table());
   EXPECT_THAT(slices, ElementsAre(SliceInfo{2, 3}));
@@ -184,10 +197,14 @@
   // from being closed, leading to an inconsistency when we try to insert the
   // final slice and it doesn't intersect with the still pending first slice.
   constexpr TrackId track{22u};
-  tracker.Scoped(2 /*ts*/, track, 0 /*cat*/, 1 /*name*/, 10 /* dur */);
-  tracker.Scoped(2 /*ts*/, track, 0 /*cat*/, 1 /*name*/, 0 /* dur */);
-  tracker.Scoped(12 /*ts*/, track, 0 /*cat*/, 1 /*name*/, 1 /* dur */);
-  tracker.Scoped(13 /*ts*/, track, 0 /*cat*/, 1 /*name*/, 1 /* dur */);
+  tracker.Scoped(2 /*ts*/, track, kNullStringId /*cat*/,
+                 StringId::Raw(1) /*name*/, 10 /* dur */);
+  tracker.Scoped(2 /*ts*/, track, kNullStringId /*cat*/,
+                 StringId::Raw(1) /*name*/, 0 /* dur */);
+  tracker.Scoped(12 /*ts*/, track, kNullStringId /*cat*/,
+                 StringId::Raw(1) /*name*/, 1 /* dur */);
+  tracker.Scoped(13 /*ts*/, track, kNullStringId /*cat*/,
+                 StringId::Raw(1) /*name*/, 1 /* dur */);
 
   auto slices = ToSliceInfo(context.storage->slice_table());
   EXPECT_THAT(slices, ElementsAre(SliceInfo{2, 10}, SliceInfo{2, 0},
@@ -201,9 +218,9 @@
 
   constexpr TrackId track_a{22u};
   constexpr TrackId track_b{23u};
-  tracker.Begin(0 /*ts*/, track_a, 0, 0);
-  tracker.Scoped(2 /*ts*/, track_b, 0, 0, 6);
-  tracker.Scoped(3 /*ts*/, track_b, 0, 0, 4);
+  tracker.Begin(0 /*ts*/, track_a, kNullStringId, kNullStringId);
+  tracker.Scoped(2 /*ts*/, track_b, kNullStringId, kNullStringId, 6);
+  tracker.Scoped(3 /*ts*/, track_b, kNullStringId, kNullStringId, 4);
   tracker.End(10 /*ts*/, track_a);
   tracker.FlushPendingSlices();
 
@@ -225,34 +242,47 @@
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
-  tracker.Scoped(50 /*ts*/, track, 11 /*cat*/, 21 /*name*/, 100 /*dur*/);
-  tracker.Begin(100 /*ts*/, track, 12 /*cat*/, 22 /*name*/);
-  tracker.Scoped(450 /*ts*/, track, 12 /*cat*/, 22 /*name*/, 100 /*dur*/);
-  tracker.End(500 /*ts*/, track, 12 /*cat*/, 22 /*name*/);
+  tracker.Scoped(50 /*ts*/, track, StringId::Raw(11) /*cat*/,
+                 StringId::Raw(21) /*name*/, 100 /*dur*/);
+  tracker.Begin(100 /*ts*/, track, StringId::Raw(12) /*cat*/,
+                StringId::Raw(22) /*name*/);
 
   // This slice should now have depth 0.
-  tracker.Begin(800 /*ts*/, track, 13 /*cat*/, 23 /*name*/);
+  tracker.Scoped(450 /*ts*/, track, StringId::Raw(12) /*cat*/,
+                 StringId::Raw(22) /*name*/, 100 /*dur*/);
+
+  // This slice should be ignored.
+  tracker.End(500 /*ts*/, track, StringId::Raw(12) /*cat*/,
+              StringId::Raw(22) /*name*/);
+
+  tracker.Begin(800 /*ts*/, track, StringId::Raw(13) /*cat*/,
+                StringId::Raw(23) /*name*/);
   // Null cat and name matches everything.
-  tracker.End(1000 /*ts*/, track, 0 /*cat*/, 0 /*name*/);
+  tracker.End(1000 /*ts*/, track, kNullStringId /*cat*/,
+              kNullStringId /*name*/);
 
   // Slice will not close if category is different.
-  tracker.Begin(1100 /*ts*/, track, 11 /*cat*/, 21 /*name*/);
-  tracker.End(1200 /*ts*/, track, 12 /*cat*/, 21 /*name*/);
+  tracker.Begin(1100 /*ts*/, track, StringId::Raw(11) /*cat*/,
+                StringId::Raw(21) /*name*/);
+  tracker.End(1200 /*ts*/, track, StringId::Raw(12) /*cat*/,
+              StringId::Raw(21) /*name*/);
 
   // Slice will not close if name is different.
-  tracker.Begin(1300 /*ts*/, track, 11 /*cat*/, 21 /*name*/);
-  tracker.End(1400 /*ts*/, track, 11 /*cat*/, 22 /*name*/);
+  tracker.Begin(1300 /*ts*/, track, StringId::Raw(11) /*cat*/,
+                StringId::Raw(21) /*name*/);
+  tracker.End(1400 /*ts*/, track, StringId::Raw(11) /*cat*/,
+              StringId::Raw(22) /*name*/);
 
   tracker.FlushPendingSlices();
 
   auto slices = ToSliceInfo(context.storage->slice_table());
-  EXPECT_THAT(slices, ElementsAre(SliceInfo{50, 100}, SliceInfo{100, 400},
+  EXPECT_THAT(slices, ElementsAre(SliceInfo{50, 100}, SliceInfo{100, 50},
                                   SliceInfo{450, 100}, SliceInfo{800, 200},
                                   SliceInfo{1100, -1}, SliceInfo{1300, 0 - 1}));
 
   EXPECT_EQ(context.storage->slice_table().depth()[0], 0u);
   EXPECT_EQ(context.storage->slice_table().depth()[1], 1u);
-  EXPECT_EQ(context.storage->slice_table().depth()[2], 2u);
+  EXPECT_EQ(context.storage->slice_table().depth()[2], 0u);
   EXPECT_EQ(context.storage->slice_table().depth()[3], 0u);
 }
 
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index 07b39c0..418e517 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -14,40 +14,44 @@
 
 import("../../../gn/test.gni")
 
-source_set("sqlite") {
-  sources = [
-    "db_sqlite_table.cc",
-    "db_sqlite_table.h",
-    "query_constraints.cc",
-    "query_constraints.h",
-    "scoped_db.h",
-    "sqlite3_str_split.cc",
-    "sqlite3_str_split.h",
-    "sqlite_table.cc",
-    "sqlite_table.h",
-    "sqlite_utils.h",
-  ]
-  deps = [
-    "../../../gn:default_deps",
-    "../../../gn:sqlite",
-    "../../../include/perfetto/trace_processor",
-    "../../base",
-    "../db:lib",
-  ]
-}
+if (enable_perfetto_trace_processor_sqlite) {
+  source_set("sqlite") {
+    sources = [
+      "db_sqlite_table.cc",
+      "db_sqlite_table.h",
+      "query_constraints.cc",
+      "query_constraints.h",
+      "scoped_db.h",
+      "sqlite3_str_split.cc",
+      "sqlite3_str_split.h",
+      "sqlite_table.cc",
+      "sqlite_table.h",
+      "sqlite_utils.h",
+    ]
+    deps = [
+      "../../../gn:default_deps",
+      "../../../gn:sqlite",
+      "../../../include/perfetto/trace_processor",
+      "../../../protos/perfetto/trace/ftrace:zero",
+      "../../base",
+      "../db:lib",
+      "../types",
+    ]
+  }
 
-perfetto_unittest_source_set("unittests") {
-  testonly = true
-  sources = [
-    "db_sqlite_table_unittest.cc",
-    "query_constraints_unittest.cc",
-    "sqlite3_str_split_unittest.cc",
-  ]
-  deps = [
-    ":sqlite",
-    "../../../gn:default_deps",
-    "../../../gn:gtest_and_gmock",
-    "../../../gn:sqlite",
-    "../../base",
-  ]
+  perfetto_unittest_source_set("unittests") {
+    testonly = true
+    sources = [
+      "db_sqlite_table_unittest.cc",
+      "query_constraints_unittest.cc",
+      "sqlite3_str_split_unittest.cc",
+    ]
+    deps = [
+      ":sqlite",
+      "../../../gn:default_deps",
+      "../../../gn:gtest_and_gmock",
+      "../../../gn:sqlite",
+      "../../base",
+    ]
+  }
 }
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index 65ed66e..83789ea 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -45,6 +45,8 @@
       return FilterOp::kIsNotNull;
     case SQLITE_INDEX_CONSTRAINT_LIKE:
       return FilterOp::kLike;
+    case SQLITE_INDEX_CONSTRAINT_GLOB:
+      return FilterOp::kGlob;
     default:
       PERFETTO_FATAL("Currently unsupported constraint");
   }
@@ -91,26 +93,31 @@
 }
 
 util::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
+  *schema = ComputeSchema(*table_, name().c_str());
+  return util::OkStatus();
+}
+
+SqliteTable::Schema DbSqliteTable::ComputeSchema(const Table& table,
+                                                 const char* table_name) {
   std::vector<SqliteTable::Column> schema_cols;
-  for (uint32_t i = 0; i < table_->GetColumnCount(); ++i) {
-    const auto& col = table_->GetColumn(i);
+  for (uint32_t i = 0; i < table.GetColumnCount(); ++i) {
+    const auto& col = table.GetColumn(i);
     schema_cols.emplace_back(i, col.name(), col.type());
   }
+
   // TODO(lalitm): this is hardcoded to be the id column but change this to be
   // more generic in the future.
-  const auto* col = table_->GetColumnByName("id");
+  const auto* col = table.GetColumnByName("id");
   if (!col) {
     PERFETTO_FATAL(
         "id column not found in %s. Currently all db Tables need to contain an "
         "id column; this constraint will be relaxed in the future.",
-        name().c_str());
+        table_name);
   }
 
   std::vector<size_t> primary_keys;
   primary_keys.emplace_back(col->index_in_table());
-
-  *schema = Schema(std::move(schema_cols), std::move(primary_keys));
-  return util::OkStatus();
+  return Schema(std::move(schema_cols), std::move(primary_keys));
 }
 
 int DbSqliteTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
@@ -215,9 +222,12 @@
     } else if (sqlite_utils::IsOpEq(c.op)) {
       // If there is only a single equality constraint, we have special logic
       // to sort by that column and then binary search if we see the constraint
-      // set often. Model this by dividing but the log of the number of rows as
+      // set often. Model this by dividing by the log of the number of rows as
       // a good approximation. Otherwise, we'll need to do a full table scan.
-      filter_cost += cs.size() == 1
+      // Alternatively, if the column is sorted, we can use the same binary
+      // search logic so we have the same low cost (even better because we don't
+      // have to sort at all).
+      filter_cost += cs.size() == 1 || col.IsSorted()
                          ? (2 * current_row_count) / log2(current_row_count)
                          : current_row_count;
 
@@ -250,11 +260,50 @@
 }
 
 std::unique_ptr<SqliteTable::Cursor> DbSqliteTable::CreateCursor() {
-  return std::unique_ptr<Cursor>(new Cursor(this));
+  return std::unique_ptr<Cursor>(new Cursor(this, table_));
 }
 
-DbSqliteTable::Cursor::Cursor(DbSqliteTable* table)
-    : SqliteTable::Cursor(table), initial_db_table_(table->table_) {}
+DbSqliteTable::Cursor::Cursor(SqliteTable* sqlite_table, const Table* table)
+    : SqliteTable::Cursor(sqlite_table), initial_db_table_(table) {}
+
+void DbSqliteTable::Cursor::TryCacheCreateSortedTable(
+    const QueryConstraints& qc,
+    FilterHistory history) {
+  if (history == FilterHistory::kDifferent) {
+    // Every time we get a new constraint set, reset the state of any caching
+    // structures.
+    sorted_cache_table_ = base::nullopt;
+    repeated_cache_count_ = 0;
+    return;
+  }
+
+  PERFETTO_DCHECK(history == FilterHistory::kSame);
+
+  // Only try and create the cached table on exactly the third time we see this
+  // constraint set.
+  constexpr uint32_t kRepeatedThreshold = 3;
+  if (repeated_cache_count_++ != kRepeatedThreshold)
+    return;
+
+  // If we have more than one constraint, we can't cache the table using
+  // this method.
+  if (qc.constraints().size() != 1)
+    return;
+
+  // If the constraing is not an equality constraint, there's little
+  // benefit to caching
+  const auto& c = qc.constraints().front();
+  if (!sqlite_utils::IsOpEq(c.op))
+    return;
+
+  // If the column is already sorted, we don't need to cache at all.
+  uint32_t col = static_cast<uint32_t>(c.column);
+  if (initial_db_table_->GetColumn(col).IsSorted())
+    return;
+
+  // Create the cached table, sorting on the column which has the constraint.
+  sorted_cache_table_ = initial_db_table_->Sort({Order{col, false}});
+}
 
 int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc,
                                   sqlite3_value** argv,
@@ -263,23 +312,9 @@
   // before the table's destructor.
   iterator_ = base::nullopt;
 
-  if (history == FilterHistory::kSame && qc.constraints().size() == 1 &&
-      sqlite_utils::IsOpEq(qc.constraints().front().op)) {
-    // If we've seen the same constraint set with a single equality constraint
-    // more than |kRepeatedThreshold| times, we assume we will see it more
-    // in the future and thus cache a table sorted on the column. That way,
-    // future equality constraints can binary search for the value instead of
-    // doing a full table scan.
-    constexpr uint32_t kRepeatedThreshold = 3;
-    if (!sorted_cache_table_ && repeated_cache_count_++ > kRepeatedThreshold) {
-      const auto& c = qc.constraints().front();
-      uint32_t col = static_cast<uint32_t>(c.column);
-      sorted_cache_table_ = initial_db_table_->Sort({Order{col, false}});
-    }
-  } else {
-    sorted_cache_table_ = base::nullopt;
-    repeated_cache_count_ = 0;
-  }
+  // Tries to create a sorted cached table which can be used to speed up
+  // filters below.
+  TryCacheCreateSortedTable(qc, history);
 
   // We reuse this vector to reduce memory allocations on nested subqueries.
   constraints_.resize(qc.constraints().size());
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 1e39653..2a606ec 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -26,9 +26,9 @@
 // Implements the SQLite table interface for db tables.
 class DbSqliteTable : public SqliteTable {
  public:
-  class Cursor final : public SqliteTable::Cursor {
+  class Cursor : public SqliteTable::Cursor {
    public:
-    explicit Cursor(DbSqliteTable* table);
+    Cursor(SqliteTable*, const Table* table);
 
     Cursor(Cursor&&) noexcept = default;
     Cursor& operator=(Cursor&&) = default;
@@ -41,12 +41,22 @@
     int Eof() override;
     int Column(sqlite3_context*, int N) override;
 
+   protected:
+    // Sets the table this class uses as the reference for all filter
+    // operations. Should be immediately followed by a call to Filter with
+    // |FilterHistory::kDifferent|.
+    void set_table(const Table* table) { initial_db_table_ = table; }
+
    private:
     enum class Mode {
       kSingleRow,
       kTable,
     };
 
+    // Tries to create a sorted table to cache in |sorted_cache_table_| if the
+    // constraint set matches the requirements.
+    void TryCacheCreateSortedTable(const QueryConstraints&, FilterHistory);
+
     const Table* SourceTable() const {
       // Try and use the sorted cache table (if it exists) to speed up the
       // sorting. Otherwise, just use the original table.
@@ -101,6 +111,9 @@
   int ModifyConstraints(QueryConstraints*) override;
   int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
 
+  static SqliteTable::Schema ComputeSchema(const Table& table,
+                                           const char* table_name);
+
   // static for testing.
   static QueryCost EstimateCost(const Table& table, const QueryConstraints& qc);
 
diff --git a/src/trace_processor/sqlite/db_sqlite_table_unittest.cc b/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
index 4f39839..2fbc3e4 100644
--- a/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
@@ -33,12 +33,18 @@
         Column("a", &a_, Column::Flag::kNoFlag, this, 1u, 0u));
     columns_.emplace_back(
         Column("sorted", &sorted_, Column::Flag::kSorted, this, 2u, 0u));
+    columns_.emplace_back(
+        Column("other", &other_, Column::Flag::kNoFlag, this, 3u, 0u));
+    columns_.emplace_back(
+        Column("other2", &other_, Column::Flag::kNoFlag, this, 4u, 0u));
   }
 
  private:
   StringPool pool_;
   SparseVector<uint32_t> a_;
   SparseVector<uint32_t> sorted_;
+  SparseVector<uint32_t> other_;
+  SparseVector<uint32_t> other2_;
 };
 
 TEST(DbSqliteTable, IdEqCheaperThanOtherEq) {
@@ -95,6 +101,27 @@
   ASSERT_GT(single_cost.rows, multi_cost.rows);
 }
 
+TEST(DbSqliteTable, MultiSortedEqCheaperThanMultiUnsortedEq) {
+  TestTable table(1234);
+
+  QueryConstraints sorted_eq;
+  sorted_eq.AddConstraint(2u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
+  sorted_eq.AddConstraint(3u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
+
+  auto sorted_cost = DbSqliteTable::EstimateCost(table, sorted_eq);
+
+  QueryConstraints unsorted_eq;
+  unsorted_eq.AddConstraint(3u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
+  unsorted_eq.AddConstraint(4u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
+
+  auto unsorted_cost = DbSqliteTable::EstimateCost(table, unsorted_eq);
+
+  // The number of rows should be the same but the cost of the sorted
+  // query should be less.
+  ASSERT_LT(sorted_cost.cost, unsorted_cost.cost);
+  ASSERT_EQ(sorted_cost.rows, unsorted_cost.rows);
+}
+
 TEST(DbSqliteTable, EmptyTableCosting) {
   TestTable table(0u);
 
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index a5f95e1..2cb7a0f 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -55,7 +55,9 @@
     size_t index() const { return index_; }
     const std::string& name() const { return name_; }
     SqlValue::Type type() const { return type_; }
+
     bool hidden() const { return hidden_; }
+    void set_hidden(bool hidden) { hidden_ = hidden; }
 
    private:
     size_t index_ = 0;
@@ -137,6 +139,8 @@
     std::string ToCreateTableStmt() const;
 
     const std::vector<Column>& columns() const { return columns_; }
+    std::vector<Column>* mutable_columns() { return &columns_; }
+
     const std::vector<size_t> primary_keys() { return primary_keys_; }
 
    private:
diff --git a/src/trace_processor/sqlite_experimental_flamegraph_table.cc b/src/trace_processor/sqlite_experimental_flamegraph_table.cc
new file mode 100644
index 0000000..12a9bb6
--- /dev/null
+++ b/src/trace_processor/sqlite_experimental_flamegraph_table.cc
@@ -0,0 +1,178 @@
+
+/*
+ * Copyright (C) 2020 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/trace_processor/sqlite_experimental_flamegraph_table.h"
+
+#include "src/trace_processor/heap_profile_tracker.h"
+#include "src/trace_processor/importers/proto/heap_graph_tracker.h"
+#include "src/trace_processor/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+SqliteExperimentalFlamegraphTable::InputValues GetInputValues(
+    const QueryConstraints& qc,
+    sqlite3_value** argv) {
+  using T = tables::ExperimentalFlamegraphNodesTable;
+
+  const auto& cs = qc.constraints();
+
+  auto ts_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::ts) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+  auto upid_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::upid) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+  auto profile_type_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::profile_type) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+
+  auto ts_idx = static_cast<uint32_t>(
+      std::distance(cs.begin(), std::find_if(cs.begin(), cs.end(), ts_fn)));
+  auto upid_idx = static_cast<uint32_t>(
+      std::distance(cs.begin(), std::find_if(cs.begin(), cs.end(), upid_fn)));
+  auto profile_type_idx = static_cast<uint32_t>(std::distance(
+      cs.begin(), std::find_if(cs.begin(), cs.end(), profile_type_fn)));
+
+  // We should always have valid indices here because BestIndex should only
+  // allow the constraint set to be chosen when we have an equality constraint
+  // on both ts and upid.
+  PERFETTO_CHECK(ts_idx < cs.size());
+  PERFETTO_CHECK(upid_idx < cs.size());
+  PERFETTO_CHECK(profile_type_idx < cs.size());
+
+  int64_t ts = sqlite3_value_int64(argv[ts_idx]);
+  UniquePid upid = static_cast<UniquePid>(sqlite3_value_int64(argv[upid_idx]));
+  std::string profile_type =
+      reinterpret_cast<const char*>(sqlite3_value_text(argv[profile_type_idx]));
+
+  return SqliteExperimentalFlamegraphTable::InputValues{ts, upid, profile_type};
+}
+
+}  // namespace
+
+SqliteExperimentalFlamegraphTable::SqliteExperimentalFlamegraphTable(
+    sqlite3*,
+    TraceProcessorContext* context)
+    : context_(context) {}
+
+SqliteExperimentalFlamegraphTable::~SqliteExperimentalFlamegraphTable() =
+    default;
+
+void SqliteExperimentalFlamegraphTable::RegisterTable(
+    sqlite3* db,
+    TraceProcessorContext* context) {
+  SqliteTable::Register<SqliteExperimentalFlamegraphTable>(
+      db, context, "experimental_flamegraph");
+}
+
+util::Status SqliteExperimentalFlamegraphTable::Init(
+    int,
+    const char* const*,
+    SqliteTable::Schema* schema) {
+  // Create an empty table for the sake of getting the schema.
+  tables::ExperimentalFlamegraphNodesTable table(nullptr, nullptr);
+  *schema = DbSqliteTable::ComputeSchema(table, name().c_str());
+
+  using T = tables::ExperimentalFlamegraphNodesTable;
+
+  // TODO(lalitm): make it so that this happens on the macro table itself.
+  auto& cols = *schema->mutable_columns();
+  cols[static_cast<uint32_t>(T::ColumnIndex::ts)].set_hidden(true);
+  cols[static_cast<uint32_t>(T::ColumnIndex::upid)].set_hidden(true);
+  cols[static_cast<uint32_t>(T::ColumnIndex::profile_type)].set_hidden(true);
+
+  return util::OkStatus();
+}
+
+int SqliteExperimentalFlamegraphTable::BestIndex(const QueryConstraints& qc,
+                                                 BestIndexInfo*) {
+  using T = tables::ExperimentalFlamegraphNodesTable;
+
+  const auto& cs = qc.constraints();
+
+  auto ts_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::ts) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+  bool has_ts_cs = std::find_if(cs.begin(), cs.end(), ts_fn) != cs.end();
+
+  auto upid_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::upid) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+  bool has_upid_cs = std::find_if(cs.begin(), cs.end(), upid_fn) != cs.end();
+
+  auto profile_type_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::profile_type) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+  bool has_profile_type_cs =
+      std::find_if(cs.begin(), cs.end(), profile_type_fn) != cs.end();
+
+  return has_ts_cs && has_upid_cs && has_profile_type_cs ? SQLITE_OK
+                                                         : SQLITE_CONSTRAINT;
+}
+
+std::unique_ptr<SqliteTable::Cursor>
+SqliteExperimentalFlamegraphTable::CreateCursor() {
+  return std::unique_ptr<Cursor>(new Cursor(this, context_));
+}
+
+SqliteExperimentalFlamegraphTable::Cursor::Cursor(
+    SqliteTable* sqlite_table,
+    TraceProcessorContext* context)
+    : DbSqliteTable::Cursor(sqlite_table, nullptr), context_(context) {}
+
+int SqliteExperimentalFlamegraphTable::Cursor::Filter(
+    const QueryConstraints& qc,
+    sqlite3_value** argv,
+    FilterHistory) {
+  // Extract the old table to free after we call the parent Filter function.
+  // We need to do this to make sure that we don't get a use-after-free for
+  // any pointers the parent is holding onto in this table.
+  auto old_table = std::move(table_);
+
+  // Get the input column values and compute the flamegraph using them.
+  values_ = GetInputValues(qc, argv);
+
+  if (values_.profile_type == "graph") {
+    auto* tracker = HeapGraphTracker::GetOrCreate(context_);
+    table_ = tracker->BuildFlamegraph(values_.ts, values_.upid);
+  }
+  if (values_.profile_type == "native") {
+    table_ = BuildNativeFlamegraph(context_->storage.get(),
+                                   values_.upid, values_.ts);
+  }
+
+  // table_ can be nullptr precisely where the constraints passed to us don't
+  // make sense. Therefore, we can just return this to SQLite.
+  if (!table_)
+    return SQLITE_CONSTRAINT;
+
+  // Set the table in the parent to the correct value and then filter.
+  DbSqliteTable::Cursor::set_table(table_.get());
+  return DbSqliteTable::Cursor::Filter(qc, argv, FilterHistory::kDifferent);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sqlite_experimental_flamegraph_table.h b/src/trace_processor/sqlite_experimental_flamegraph_table.h
new file mode 100644
index 0000000..e531814
--- /dev/null
+++ b/src/trace_processor/sqlite_experimental_flamegraph_table.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 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_TRACE_PROCESSOR_SQLITE_EXPERIMENTAL_FLAMEGRAPH_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_EXPERIMENTAL_FLAMEGRAPH_TABLE_H_
+
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+
+#include "src/trace_processor/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class SqliteExperimentalFlamegraphTable : public SqliteTable {
+ public:
+  struct InputValues {
+    int64_t ts;
+    UniquePid upid;
+    std::string profile_type;
+  };
+
+  class Cursor : public DbSqliteTable::Cursor {
+   public:
+    Cursor(SqliteTable*, TraceProcessorContext*);
+
+    int Filter(const QueryConstraints& qc,
+               sqlite3_value** argv,
+               FilterHistory) override;
+
+   private:
+    TraceProcessorContext* context_ = nullptr;
+
+    std::unique_ptr<Table> table_;
+    InputValues values_ = {};
+  };
+
+  SqliteExperimentalFlamegraphTable(sqlite3*, TraceProcessorContext*);
+  ~SqliteExperimentalFlamegraphTable() override;
+
+  static void RegisterTable(sqlite3* db, TraceProcessorContext* storage);
+
+  // SqliteTable implementation.
+  util::Status Init(int,
+                    const char* const*,
+                    SqliteTable::Schema*) override final;
+  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
+  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+
+ private:
+  friend class Cursor;
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_EXPERIMENTAL_FLAMEGRAPH_TABLE_H_
diff --git a/src/trace_processor/raw_table.cc b/src/trace_processor/sqlite_raw_table.cc
similarity index 78%
rename from src/trace_processor/raw_table.cc
rename to src/trace_processor/sqlite_raw_table.cc
index ac8127f..af66914 100644
--- a/src/trace_processor/raw_table.cc
+++ b/src/trace_processor/sqlite_raw_table.cc
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/raw_table.h"
+#include "src/trace_processor/sqlite_raw_table.h"
 
 #include <inttypes.h>
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/gfp_flags.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/gfp_flags.h"
+#include "src/trace_processor/types/variadic.h"
 
 #include "protos/perfetto/trace/ftrace/binder.pbzero.h"
 #include "protos/perfetto/trace/ftrace/clk.pbzero.h"
@@ -50,10 +50,10 @@
 }
 }  // namespace
 
-RawTable::RawTable(sqlite3* db, const TraceStorage* storage)
-    : storage_(storage) {
+SqliteRawTable::SqliteRawTable(sqlite3* db, const TraceStorage* storage)
+    : DbSqliteTable(db, &storage->raw_table()), storage_(storage) {
   auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
-    auto* thiz = static_cast<RawTable*>(sqlite3_user_data(ctx));
+    auto* thiz = static_cast<SqliteRawTable*>(sqlite3_user_data(ctx));
     thiz->ToSystrace(ctx, argc, argv);
   };
   sqlite3_create_function(db, "to_ftrace", 1,
@@ -61,41 +61,13 @@
                           nullptr);
 }
 
-void RawTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<RawTable>(db, storage, "raw");
+SqliteRawTable::~SqliteRawTable() = default;
+
+void SqliteRawTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
+  SqliteTable::Register<SqliteRawTable>(db, storage, "raw");
 }
 
-StorageSchema RawTable::CreateStorageSchema() {
-  const auto& raw = storage_->raw_events();
-  return StorageSchema::Builder()
-      .AddGenericNumericColumn("id", RowAccessor())
-      .AddOrderedNumericColumn("ts", &raw.timestamps())
-      .AddStringColumn("name", &raw.name_ids(), &storage_->string_pool())
-      .AddNumericColumn("cpu", &raw.cpus())
-      .AddNumericColumn("utid", &raw.utids())
-      .AddNumericColumn("arg_set_id", &raw.arg_set_ids())
-      .Build({"name", "ts"});
-}
-
-uint32_t RawTable::RowCount() {
-  return static_cast<uint32_t>(storage_->raw_events().raw_event_count());
-}
-
-int RawTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
-  info->estimated_cost = RowCount();
-  info->sqlite_omit_order_by = true;
-
-  // Only the string columns are handled by SQLite
-  size_t name_index = schema().ColumnIndexFromName("name");
-  for (size_t i = 0; i < qc.constraints().size(); i++) {
-    info->sqlite_omit_constraint[i] =
-        qc.constraints()[i].column != static_cast<int>(name_index);
-  }
-
-  return SQLITE_OK;
-}
-
-bool RawTable::ParseGfpFlags(Variadic value, base::StringWriter* writer) {
+bool SqliteRawTable::ParseGfpFlags(Variadic value, base::StringWriter* writer) {
   const auto& metadata_table = storage_->metadata_table();
 
   auto opt_name_idx = metadata_table.name().IndexOf(
@@ -118,15 +90,21 @@
   return true;
 }
 
-void RawTable::FormatSystraceArgs(NullTermStringView event_name,
-                                  ArgSetId arg_set_id,
-                                  base::StringWriter* writer) {
-  const auto& set_ids = storage_->args().set_ids();
-  auto lb = std::lower_bound(set_ids.begin(), set_ids.end(), arg_set_id);
-  auto ub = std::find(lb, set_ids.end(), arg_set_id + 1);
+void SqliteRawTable::FormatSystraceArgs(NullTermStringView event_name,
+                                        ArgSetId arg_set_id,
+                                        base::StringWriter* writer) {
+  const auto& set_ids = storage_->arg_table().arg_set_id();
 
-  auto start_row = static_cast<uint32_t>(std::distance(set_ids.begin(), lb));
-  auto end_row = static_cast<uint32_t>(std::distance(set_ids.begin(), ub));
+  // TODO(lalitm): this code is quite hacky for performance reasons. We assume
+  // that the row map is a contiguous range (which is always the case
+  // because arg_set_ids are contiguous by definition). We also assume that
+  // the proto field order is also the order of insertion (which happens to
+  // be true but proabably shouldn't be relied on).
+  RowMap rm = storage_->arg_table().FilterToRowMap({set_ids.eq(arg_set_id)});
+  if (rm.empty())
+    return;
+
+  uint32_t start_row = rm.Get(0);
   using ValueWriter = std::function<void(const Variadic&)>;
   auto write_value = [this, writer](const Variadic& value) {
     switch (value.type) {
@@ -159,24 +137,22 @@
   };
   auto write_value_at_index = [this, start_row](uint32_t arg_idx,
                                                 ValueWriter value_fn) {
-    value_fn(storage_->args().arg_values()[start_row + arg_idx]);
+    value_fn(storage_->GetArgValue(start_row + arg_idx));
   };
   auto write_arg = [this, writer, start_row](uint32_t arg_idx,
                                              ValueWriter value_fn) {
     uint32_t arg_row = start_row + arg_idx;
-    const auto& args = storage_->args();
-    const auto& key = storage_->GetString(args.keys()[arg_row]);
+    const auto& args = storage_->arg_table();
+    const auto& key = storage_->GetString(args.key()[arg_row]);
+    auto value = storage_->GetArgValue(arg_row);
 
     writer->AppendChar(' ');
     writer->AppendString(key.c_str(), key.size());
     writer->AppendChar('=');
 
-    if (key == "gfp_flags" &&
-        ParseGfpFlags(args.arg_values()[arg_row], writer)) {
+    if (key == "gfp_flags" && ParseGfpFlags(value, writer))
       return;
-    }
-
-    value_fn(args.arg_values()[arg_row]);
+    value_fn(value);
   };
 
   if (event_name == "sched_switch") {
@@ -234,17 +210,17 @@
     write_value_at_index(BT::kToThreadFieldNumber - 1, write_value);
     write_arg(BT::kReplyFieldNumber - 1, write_value);
     writer->AppendString(" flags=0x");
-    write_value_at_index(
-        BT::kFlagsFieldNumber - 1, [writer](const Variadic& value) {
-          PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-          writer->AppendHexInt(value.uint_value);
-        });
+    write_value_at_index(BT::kFlagsFieldNumber - 1,
+                         [writer](const Variadic& value) {
+                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+                           writer->AppendHexInt(value.uint_value);
+                         });
     writer->AppendString(" code=0x");
-    write_value_at_index(
-        BT::kCodeFieldNumber - 1, [writer](const Variadic& value) {
-          PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-          writer->AppendHexInt(value.uint_value);
-        });
+    write_value_at_index(BT::kCodeFieldNumber - 1,
+                         [writer](const Variadic& value) {
+                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+                           writer->AppendHexInt(value.uint_value);
+                         });
     return;
   } else if (event_name == "binder_transaction_alloc_buf") {
     using BTAB = protos::pbzero::BinderTransactionAllocBufFtraceEvent;
@@ -291,9 +267,8 @@
   } else if (event_name == "print") {
     // 'ip' may be the first field or it may be dropped. We only care
     // about the 'buf' field which will always appear last.
-    uint32_t arg_row = end_row - 1;
-    const auto& args = storage_->args();
-    const auto& value = args.arg_values()[arg_row];
+    uint32_t arg_row = rm.Get(rm.size() - 1);
+    const auto& value = storage_->GetArgValue(arg_row);
     const auto& str = storage_->GetString(value.string_value);
     // If the last character is a newline in a print, just drop it.
     auto chars_to_print = !str.empty() && str.c_str()[str.size() - 1] == '\n'
@@ -365,38 +340,37 @@
     return;
   }
 
-  uint32_t arg = 0;
-  for (auto it = lb; it != ub; it++) {
-    write_arg(arg++, write_value);
+  for (auto it = rm.IterateRows(); it; it.Next()) {
+    write_arg(it.index(), write_value);
   }
 }
 
-void RawTable::ToSystrace(sqlite3_context* ctx,
-                          int argc,
-                          sqlite3_value** argv) {
+void SqliteRawTable::ToSystrace(sqlite3_context* ctx,
+                                int argc,
+                                sqlite3_value** argv) {
   if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
     sqlite3_result_error(ctx, "Usage: to_ftrace(id)", -1);
     return;
   }
   uint32_t row = static_cast<uint32_t>(sqlite3_value_int64(argv[0]));
-  const auto& raw_evts = storage_->raw_events();
+  const auto& raw_evts = storage_->raw_table();
 
-  UniqueTid utid = raw_evts.utids()[row];
-  const auto& thread = storage_->GetThread(utid);
+  UniqueTid utid = raw_evts.utid()[row];
   uint32_t tgid = 0;
-  if (thread.upid.has_value()) {
-    tgid = storage_->GetProcess(thread.upid.value()).pid;
+  auto opt_upid = storage_->thread_table().upid()[utid];
+  if (opt_upid.has_value()) {
+    tgid = storage_->process_table().pid()[*opt_upid];
   }
-  const auto& name = storage_->GetString(thread.name_id);
+  const auto& name = storage_->GetString(storage_->thread_table().name()[utid]);
 
   char line[4096];
   base::StringWriter writer(line, sizeof(line));
 
-  ftrace_utils::FormatSystracePrefix(raw_evts.timestamps()[row],
-                                     raw_evts.cpus()[row], thread.tid, tgid,
+  ftrace_utils::FormatSystracePrefix(raw_evts.ts()[row], raw_evts.cpu()[row],
+                                     storage_->thread_table().tid()[utid], tgid,
                                      base::StringView(name), &writer);
 
-  const auto& event_name = storage_->GetString(raw_evts.name_ids()[row]);
+  const auto& event_name = storage_->GetString(raw_evts.name()[row]);
   writer.AppendChar(' ');
   if (event_name == "print") {
     writer.AppendString("tracing_mark_write");
@@ -405,7 +379,7 @@
   }
   writer.AppendChar(':');
 
-  FormatSystraceArgs(event_name, raw_evts.arg_set_ids()[row], &writer);
+  FormatSystraceArgs(event_name, raw_evts.arg_set_id()[row], &writer);
   sqlite3_result_text(ctx, writer.CreateStringCopy(), -1, free);
 }
 
diff --git a/src/trace_processor/raw_table.h b/src/trace_processor/sqlite_raw_table.h
similarity index 74%
rename from src/trace_processor/raw_table.h
rename to src/trace_processor/sqlite_raw_table.h
index e4506b4..99ebff2 100644
--- a/src/trace_processor/raw_table.h
+++ b/src/trace_processor/sqlite_raw_table.h
@@ -14,27 +14,25 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_RAW_TABLE_H_
-#define SRC_TRACE_PROCESSOR_RAW_TABLE_H_
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_RAW_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_RAW_TABLE_H_
 
 #include "perfetto/base/logging.h"
-#include "src/trace_processor/storage_table.h"
+#include "perfetto/ext/base/string_writer.h"
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
 #include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-class RawTable : public StorageTable {
+class SqliteRawTable : public DbSqliteTable {
  public:
+  SqliteRawTable(sqlite3*, const TraceStorage*);
+  virtual ~SqliteRawTable();
+
   static void RegisterTable(sqlite3* db, const TraceStorage* storage);
 
-  RawTable(sqlite3*, const TraceStorage*);
-
-  // Table implementation.
-  StorageSchema CreateStorageSchema() override;
-  uint32_t RowCount() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
  private:
   void FormatSystraceArgs(NullTermStringView event_name,
                           ArgSetId arg_set_id,
@@ -48,4 +46,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_RAW_TABLE_H_
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_RAW_TABLE_H_
diff --git a/src/trace_processor/stack_profile_tracker.cc b/src/trace_processor/stack_profile_tracker.cc
index e130710..10138f3 100644
--- a/src/trace_processor/stack_profile_tracker.cc
+++ b/src/trace_processor/stack_profile_tracker.cc
@@ -43,14 +43,14 @@
   string_map_.emplace(id, str.ToStdString());
 }
 
-base::Optional<int64_t> StackProfileTracker::AddMapping(
+base::Optional<MappingId> StackProfileTracker::AddMapping(
     SourceMappingId id,
     const SourceMapping& mapping,
     const InternLookup* intern_lookup) {
   std::string path;
   for (SourceStringId str_id : mapping.name_ids) {
-    auto opt_str =
-        FindString(str_id, intern_lookup, InternedStringType::kMappingPath);
+    auto opt_str = FindOrInsertString(str_id, intern_lookup,
+                                      InternedStringType::kMappingPath);
     if (!opt_str)
       break;
     path += "/" + *opt_str;
@@ -60,7 +60,7 @@
                                           InternedStringType::kBuildId);
   if (!opt_build_id) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
-    PERFETTO_DFATAL("Invalid string.");
+    PERFETTO_DLOG("Invalid string.");
     return base::nullopt;
   }
   const StringId raw_build_id = opt_build_id.value();
@@ -68,9 +68,18 @@
       context_->storage->GetString(raw_build_id);
   StringId build_id = GetEmptyStringId();
   if (raw_build_id_str.size() > 0) {
-    std::string hex_build_id =
-        base::ToHex(raw_build_id_str.c_str(), raw_build_id_str.size());
-    build_id = context_->storage->InternString(base::StringView(hex_build_id));
+    // If the build_id is 33 characters long, we assume it's a Breakpad debug
+    // identifier which is already in Hex and doesn't need conversion.
+    // TODO(b/148109467): Remove workaround once all active Chrome versions
+    // write raw bytes instead of a string as build_id.
+    if (raw_build_id_str.size() == 33) {
+      build_id = raw_build_id;
+    } else {
+      std::string hex_build_id =
+          base::ToHex(raw_build_id_str.c_str(), raw_build_id_str.size());
+      build_id =
+          context_->storage->InternString(base::StringView(hex_build_id));
+    }
   }
 
   tables::StackProfileMappingTable::Row row{
@@ -84,42 +93,40 @@
 
   tables::StackProfileMappingTable* mappings =
       context_->storage->mutable_stack_profile_mapping_table();
-  int64_t cur_row = -1;
+  base::Optional<MappingId> cur_id;
   auto it = mapping_idx_.find(row);
   if (it != mapping_idx_.end()) {
-    cur_row = it->second;
+    cur_id = it->second;
   } else {
-    std::vector<int64_t> db_mappings =
+    std::vector<MappingId> db_mappings =
         context_->storage->FindMappingRow(row.name, row.build_id);
-    for (const int64_t preexisting_mapping : db_mappings) {
-      PERFETTO_DCHECK(preexisting_mapping >= 0);
-      uint32_t preexisting_row_id = static_cast<uint32_t>(preexisting_mapping);
-      tables::StackProfileMappingTable::Row preexisting_row{
-          mappings->build_id()[preexisting_row_id],
-          mappings->exact_offset()[preexisting_row_id],
-          mappings->start_offset()[preexisting_row_id],
-          mappings->start()[preexisting_row_id],
-          mappings->end()[preexisting_row_id],
-          mappings->load_bias()[preexisting_row_id],
-          mappings->name()[preexisting_row_id]};
+    for (const MappingId preexisting_mapping : db_mappings) {
+      uint32_t preexisting_row = *mappings->id().IndexOf(preexisting_mapping);
+      tables::StackProfileMappingTable::Row preexisting_data{
+          mappings->build_id()[preexisting_row],
+          mappings->exact_offset()[preexisting_row],
+          mappings->start_offset()[preexisting_row],
+          mappings->start()[preexisting_row],
+          mappings->end()[preexisting_row],
+          mappings->load_bias()[preexisting_row],
+          mappings->name()[preexisting_row]};
 
-      if (row == preexisting_row) {
-        cur_row = preexisting_mapping;
+      if (row == preexisting_data) {
+        cur_id = preexisting_mapping;
       }
     }
-    if (cur_row == -1) {
+    if (!cur_id) {
       MappingId mapping_id = mappings->Insert(row);
-      uint32_t mapping_row = *mappings->id().IndexOf(mapping_id);
-      context_->storage->InsertMappingRow(row.name, row.build_id, mapping_row);
-      cur_row = mapping_row;
+      context_->storage->InsertMappingId(row.name, row.build_id, mapping_id);
+      cur_id = mapping_id;
     }
-    mapping_idx_.emplace(row, cur_row);
+    mapping_idx_.emplace(row, *cur_id);
   }
-  mappings_.emplace(id, cur_row);
-  return cur_row;
+  mapping_ids_.emplace(id, *cur_id);
+  return cur_id;
 }
 
-base::Optional<int64_t> StackProfileTracker::AddFrame(
+base::Optional<FrameId> StackProfileTracker::AddFrame(
     SourceFrameId id,
     const SourceFrame& frame,
     const InternLookup* intern_lookup) {
@@ -127,106 +134,101 @@
                                         InternedStringType::kFunctionName);
   if (!opt_str_id) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
-    PERFETTO_DFATAL("Invalid string.");
+    PERFETTO_DLOG("Invalid string.");
     return base::nullopt;
   }
   const StringId& str_id = opt_str_id.value();
 
-  auto maybe_mapping = FindMapping(frame.mapping_id, intern_lookup);
+  auto maybe_mapping = FindOrInsertMapping(frame.mapping_id, intern_lookup);
   if (!maybe_mapping) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
     PERFETTO_ELOG("Invalid mapping for frame %" PRIu64, id);
     return base::nullopt;
   }
-  int64_t mapping_row = *maybe_mapping;
+  MappingId mapping_id = *maybe_mapping;
 
-  tables::StackProfileFrameTable::Row row{str_id, mapping_row,
+  tables::StackProfileFrameTable::Row row{str_id, mapping_id.value,
                                           static_cast<int64_t>(frame.rel_pc)};
 
   auto* frames = context_->storage->mutable_stack_profile_frame_table();
 
-  int64_t cur_row = -1;
+  base::Optional<FrameId> cur_id;
   auto it = frame_idx_.find(row);
   if (it != frame_idx_.end()) {
-    cur_row = it->second;
+    cur_id = it->second;
   } else {
-    std::vector<int64_t> db_frames = context_->storage->FindFrameRow(
-        static_cast<size_t>(mapping_row), frame.rel_pc);
-    for (const int64_t preexisting_frame : db_frames) {
-      PERFETTO_DCHECK(preexisting_frame >= 0);
-      uint32_t preexisting_row_id = static_cast<uint32_t>(preexisting_frame);
+    std::vector<FrameId> db_frames =
+        context_->storage->FindFrameIds(mapping_id, frame.rel_pc);
+    for (const FrameId preexisting_frame : db_frames) {
+      uint32_t preexisting_row_id = preexisting_frame.value;
       tables::StackProfileFrameTable::Row preexisting_row{
           frames->name()[preexisting_row_id],
           frames->mapping()[preexisting_row_id],
           frames->rel_pc()[preexisting_row_id]};
 
       if (row == preexisting_row) {
-        cur_row = preexisting_frame;
+        cur_id = preexisting_frame;
       }
     }
-    if (cur_row == -1) {
-      auto new_id = frames->Insert(row);
-      cur_row = *frames->id().IndexOf(new_id);
-      context_->storage->InsertFrameRow(static_cast<size_t>(row.mapping),
-                                        static_cast<uint64_t>(row.rel_pc),
-                                        static_cast<uint32_t>(cur_row));
+    if (!cur_id) {
+      cur_id = frames->Insert(row);
+      context_->storage->InsertFrameRow(
+          mapping_id, static_cast<uint64_t>(row.rel_pc), *cur_id);
     }
-    frame_idx_.emplace(row, cur_row);
+    frame_idx_.emplace(row, *cur_id);
   }
-  frames_.emplace(id, cur_row);
-  return cur_row;
+  frame_ids_.emplace(id, *cur_id);
+  return cur_id;
 }
 
-base::Optional<int64_t> StackProfileTracker::AddCallstack(
+base::Optional<CallsiteId> StackProfileTracker::AddCallstack(
     SourceCallstackId id,
     const SourceCallstack& frame_ids,
     const InternLookup* intern_lookup) {
-  // TODO(fmayer): This should be NULL.
-  int64_t parent_id = -1;
-  for (size_t depth = 0; depth < frame_ids.size(); ++depth) {
-    std::vector<SourceFrameId> frame_subset = frame_ids;
-    frame_subset.resize(depth + 1);
-    auto self_it = callstacks_from_frames_.find(frame_subset);
-    if (self_it != callstacks_from_frames_.end()) {
-      parent_id = self_it->second;
-      continue;
-    }
+  if (frame_ids.size() == 0)
+    return base::nullopt;
 
-    SourceFrameId frame_id = frame_ids[depth];
-    auto maybe_frame_row = FindFrame(frame_id, intern_lookup);
-    if (!maybe_frame_row) {
+  // TODO(fmayer): This should be NULL.
+  base::Optional<CallsiteId> parent_id;
+  for (size_t depth = 0; depth < frame_ids.size(); ++depth) {
+    auto maybe_frame_id = FindOrInsertFrame(frame_ids[depth], intern_lookup);
+    if (!maybe_frame_id) {
       context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
       PERFETTO_ELOG("Unknown frame in callstack; ignoring.");
       return base::nullopt;
     }
-    int64_t frame_row = *maybe_frame_row;
+    FrameId frame_id = *maybe_frame_id;
 
+    // TODO(fmayer): Store roots as having null parent_id instead of -1.
+    int64_t db_parent_id = -1;
+    if (parent_id)
+      db_parent_id = parent_id->value;
     tables::StackProfileCallsiteTable::Row row{static_cast<int64_t>(depth),
-                                               parent_id, frame_row};
+                                               db_parent_id, frame_id.value};
 
-    int64_t self_id;
+    CallsiteId self_id;
     auto callsite_it = callsite_idx_.find(row);
     if (callsite_it != callsite_idx_.end()) {
       self_id = callsite_it->second;
     } else {
       auto* callsite =
           context_->storage->mutable_stack_profile_callsite_table();
-      auto callsite_id = callsite->Insert(row);
-      self_id = callsite_id.value;
+      self_id = callsite->Insert(row);
       callsite_idx_.emplace(row, self_id);
     }
     parent_id = self_id;
   }
-  callstacks_.emplace(id, parent_id);
+  PERFETTO_DCHECK(parent_id);  // The loop ran at least once.
+  callstack_ids_.emplace(id, *parent_id);
   return parent_id;
 }
 
-int64_t StackProfileTracker::GetDatabaseFrameIdForTesting(
+FrameId StackProfileTracker::GetDatabaseFrameIdForTesting(
     SourceFrameId frame_id) {
-  auto it = frames_.find(frame_id);
-  if (it == frames_.end()) {
-    PERFETTO_DFATAL("Invalid frame.");
-    return -1;
+  auto it = frame_ids_.find(frame_id);
+  if (it == frame_ids_.end()) {
+    PERFETTO_DLOG("Invalid frame.");
+    return {};
   }
   return it->second;
 }
@@ -238,14 +240,14 @@
   if (id == 0)
     return GetEmptyStringId();
 
-  auto opt_str = FindString(id, intern_lookup, type);
+  auto opt_str = FindOrInsertString(id, intern_lookup, type);
   if (!opt_str)
     return GetEmptyStringId();
 
   return context_->storage->InternString(base::StringView(*opt_str));
 }
 
-base::Optional<std::string> StackProfileTracker::FindString(
+base::Optional<std::string> StackProfileTracker::FindOrInsertString(
     SourceStringId id,
     const InternLookup* intern_lookup,
     StackProfileTracker::InternedStringType type) {
@@ -259,7 +261,7 @@
       if (!str) {
         context_->storage->IncrementStats(
             stats::stackprofile_invalid_string_id);
-        PERFETTO_DFATAL("Invalid string.");
+        PERFETTO_DLOG("Invalid string.");
         return base::nullopt;
       }
       return str->ToStdString();
@@ -270,12 +272,12 @@
   return it->second;
 }
 
-base::Optional<int64_t> StackProfileTracker::FindMapping(
+base::Optional<MappingId> StackProfileTracker::FindOrInsertMapping(
     SourceMappingId mapping_id,
     const InternLookup* intern_lookup) {
-  base::Optional<int64_t> res;
-  auto it = mappings_.find(mapping_id);
-  if (it == mappings_.end()) {
+  base::Optional<MappingId> res;
+  auto it = mapping_ids_.find(mapping_id);
+  if (it == mapping_ids_.end()) {
     if (intern_lookup) {
       auto interned_mapping = intern_lookup->GetMapping(mapping_id);
       if (interned_mapping) {
@@ -285,19 +287,19 @@
     }
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
     PERFETTO_ELOG("Unknown mapping %" PRIu64 " : %zu", mapping_id,
-                  mappings_.size());
+                  mapping_ids_.size());
     return res;
   }
   res = it->second;
   return res;
 }
 
-base::Optional<int64_t> StackProfileTracker::FindFrame(
+base::Optional<FrameId> StackProfileTracker::FindOrInsertFrame(
     SourceFrameId frame_id,
     const InternLookup* intern_lookup) {
-  base::Optional<int64_t> res;
-  auto it = frames_.find(frame_id);
-  if (it == frames_.end()) {
+  base::Optional<FrameId> res;
+  auto it = frame_ids_.find(frame_id);
+  if (it == frame_ids_.end()) {
     if (intern_lookup) {
       auto interned_frame = intern_lookup->GetFrame(frame_id);
       if (interned_frame) {
@@ -306,28 +308,28 @@
       }
     }
     context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
-    PERFETTO_DFATAL("Unknown frame %" PRIu64 " : %zu", frame_id,
-                    frames_.size());
+    PERFETTO_DLOG("Unknown frame %" PRIu64 " : %zu", frame_id,
+                  frame_ids_.size());
     return res;
   }
   res = it->second;
   return res;
 }
 
-base::Optional<int64_t> StackProfileTracker::FindCallstack(
+base::Optional<CallsiteId> StackProfileTracker::FindOrInsertCallstack(
     SourceCallstackId callstack_id,
     const InternLookup* intern_lookup) {
-  base::Optional<int64_t> res;
-  auto it = callstacks_.find(callstack_id);
-  if (it == callstacks_.end()) {
+  base::Optional<CallsiteId> res;
+  auto it = callstack_ids_.find(callstack_id);
+  if (it == callstack_ids_.end()) {
     auto interned_callstack = intern_lookup->GetCallstack(callstack_id);
     if (interned_callstack) {
       res = AddCallstack(callstack_id, *interned_callstack, intern_lookup);
       return res;
     }
     context_->storage->IncrementStats(stats::stackprofile_invalid_callstack_id);
-    PERFETTO_DFATAL("Unknown callstack %" PRIu64 " : %zu", callstack_id,
-                    callstacks_.size());
+    PERFETTO_DLOG("Unknown callstack %" PRIu64 " : %zu", callstack_id,
+                  callstack_ids_.size());
     return res;
   }
   res = it->second;
@@ -336,10 +338,9 @@
 
 void StackProfileTracker::ClearIndices() {
   string_map_.clear();
-  mappings_.clear();
-  callstacks_from_frames_.clear();
-  callstacks_.clear();
-  frames_.clear();
+  mapping_ids_.clear();
+  callstack_ids_.clear();
+  frame_ids_.clear();
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/stack_profile_tracker.h b/src/trace_processor/stack_profile_tracker.h
index 8c592f5..00357ce 100644
--- a/src/trace_processor/stack_profile_tracker.h
+++ b/src/trace_processor/stack_profile_tracker.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_STACK_PROFILE_TRACKER_H_
 
 #include <deque>
+#include <unordered_map>
 
 #include "perfetto/ext/base/optional.h"
 
@@ -38,6 +39,41 @@
 };
 
 template <>
+struct hash<std::pair<uint32_t, perfetto::trace_processor::CallsiteId>> {
+  using argument_type =
+      std::pair<uint32_t, perfetto::trace_processor::CallsiteId>;
+  using result_type = size_t;
+
+  result_type operator()(const argument_type& p) const {
+    return std::hash<uint32_t>{}(p.first) ^
+           std::hash<uint32_t>{}(p.second.value);
+  }
+};
+
+template <>
+struct hash<std::pair<uint32_t, perfetto::trace_processor::MappingId>> {
+  using argument_type =
+      std::pair<uint32_t, perfetto::trace_processor::MappingId>;
+  using result_type = size_t;
+
+  result_type operator()(const argument_type& p) const {
+    return std::hash<uint32_t>{}(p.first) ^
+           std::hash<uint32_t>{}(p.second.value);
+  }
+};
+
+template <>
+struct hash<std::pair<uint32_t, perfetto::trace_processor::FrameId>> {
+  using argument_type = std::pair<uint32_t, perfetto::trace_processor::FrameId>;
+  using result_type = size_t;
+
+  result_type operator()(const argument_type& p) const {
+    return std::hash<uint32_t>{}(p.first) ^
+           std::hash<uint32_t>{}(p.second.value);
+  }
+};
+
+template <>
 struct hash<std::vector<uint64_t>> {
   using argument_type = std::vector<uint64_t>;
   using result_type = size_t;
@@ -118,19 +154,19 @@
   ~StackProfileTracker();
 
   void AddString(SourceStringId, base::StringView);
-  base::Optional<int64_t> AddMapping(
+  base::Optional<MappingId> AddMapping(
       SourceMappingId,
       const SourceMapping&,
       const InternLookup* intern_lookup = nullptr);
-  base::Optional<int64_t> AddFrame(SourceFrameId,
+  base::Optional<FrameId> AddFrame(SourceFrameId,
                                    const SourceFrame&,
                                    const InternLookup* intern_lookup = nullptr);
-  base::Optional<int64_t> AddCallstack(
+  base::Optional<CallsiteId> AddCallstack(
       SourceCallstackId,
       const SourceCallstack&,
       const InternLookup* intern_lookup = nullptr);
 
-  int64_t GetDatabaseFrameIdForTesting(SourceFrameId);
+  FrameId GetDatabaseFrameIdForTesting(SourceFrameId);
 
   // Gets the row number of string / mapping / frame / callstack previously
   // added through AddString / AddMapping/ AddFrame / AddCallstack.
@@ -146,15 +182,19 @@
       SourceStringId,
       const InternLookup* intern_lookup,
       InternedStringType type);
-  base::Optional<std::string> FindString(SourceStringId,
-                                         const InternLookup* intern_lookup,
-                                         InternedStringType type);
-  base::Optional<int64_t> FindMapping(SourceMappingId,
-                                      const InternLookup* intern_lookup);
-  base::Optional<int64_t> FindFrame(SourceFrameId,
-                                    const InternLookup* intern_lookup);
-  base::Optional<int64_t> FindCallstack(SourceCallstackId,
-                                        const InternLookup* intern_lookup);
+  base::Optional<std::string> FindOrInsertString(
+      SourceStringId,
+      const InternLookup* intern_lookup,
+      InternedStringType type);
+  base::Optional<MappingId> FindOrInsertMapping(
+      SourceMappingId,
+      const InternLookup* intern_lookup);
+  base::Optional<FrameId> FindOrInsertFrame(SourceFrameId,
+                                            const InternLookup* intern_lookup);
+
+  base::Optional<CallsiteId> FindOrInsertCallstack(
+      SourceCallstackId,
+      const InternLookup* intern_lookup);
 
   // Clear indices when they're no longer needed.
   void ClearIndices();
@@ -163,17 +203,21 @@
   StringId GetEmptyStringId();
 
   std::unordered_map<SourceStringId, std::string> string_map_;
-  std::unordered_map<SourceMappingId, int64_t> mappings_;
-  std::unordered_map<SourceFrameId, int64_t> frames_;
-  std::unordered_map<SourceCallstack, int64_t> callstacks_from_frames_;
-  std::unordered_map<SourceCallstackId, int64_t> callstacks_;
+
+  // Mapping from ID of mapping / frame / callstack in original trace and the
+  // index in the respective table it was inserted into.
+  std::unordered_map<SourceMappingId, MappingId> mapping_ids_;
+  std::unordered_map<SourceFrameId, FrameId> frame_ids_;
+  std::unordered_map<SourceCallstackId, CallsiteId> callstack_ids_;
 
   // TODO(oysteine): Share these indices between the StackProfileTrackers,
   // since they're not sequence-specific.
-  std::unordered_map<tables::StackProfileMappingTable::Row, int64_t>
+  //
+  // Mapping from content of database row to the index of the raw.
+  std::unordered_map<tables::StackProfileMappingTable::Row, MappingId>
       mapping_idx_;
-  std::unordered_map<tables::StackProfileFrameTable::Row, int64_t> frame_idx_;
-  std::unordered_map<tables::StackProfileCallsiteTable::Row, int64_t>
+  std::unordered_map<tables::StackProfileFrameTable::Row, FrameId> frame_idx_;
+  std::unordered_map<tables::StackProfileCallsiteTable::Row, CallsiteId>
       callsite_idx_;
 
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 19b35b3..e9d614a 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -108,6 +108,7 @@
   F(vmstat_unknown_keys,                      kSingle,  kError,    kAnalysis), \
   F(vulkan_allocations_invalid_string_id,     kSingle,  kError,    kTrace),    \
   F(clock_sync_failure,                       kSingle,  kError,    kAnalysis), \
+  F(clock_sync_cache_miss,                    kSingle,  kInfo,     kAnalysis), \
   F(process_tracker_errors,                   kSingle,  kError,    kAnalysis), \
   F(json_tokenizer_failure,                   kSingle,  kError,    kTrace),    \
   F(heap_graph_invalid_string_id,             kIndexed, kError,    kTrace),    \
@@ -116,6 +117,8 @@
   F(heap_graph_missing_packet,                kIndexed, kDataLoss, kTrace),    \
   F(heapprofd_buffer_corrupted,               kIndexed, kError,    kTrace),    \
   F(heapprofd_buffer_overran,                 kIndexed, kDataLoss, kTrace),    \
+  F(heapprofd_client_disconnected,            kIndexed, kInfo,     kTrace),    \
+  F(heapprofd_malformed_packet,               kIndexed, kError,    kTrace),    \
   F(heapprofd_missing_packet,                 kSingle,  kError,    kTrace),    \
   F(heapprofd_rejected_concurrent,            kIndexed, kError,    kTrace),    \
   F(metatrace_overruns,                       kSingle,  kError,    kTrace),    \
@@ -150,6 +153,9 @@
   kAnalysis
 };
 
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
 // Declares an enum of literals (one for each stat). The enum values of each
 // literal corresponds to the string index in the arrays below.
 #define PERFETTO_TP_STATS_ENUM(name, ...) name
diff --git a/src/trace_processor/syscall_tracker.cc b/src/trace_processor/syscall_tracker.cc
index 609284d..5991497 100644
--- a/src/trace_processor/syscall_tracker.cc
+++ b/src/trace_processor/syscall_tracker.cc
@@ -83,7 +83,7 @@
   }
 
   for (size_t i = 0; i < kMaxSyscalls; i++) {
-    StringId id = 0;
+    StringId id = kNullStringId;
     if (i < num_syscalls && syscall_table[i] && *syscall_table[i]) {
       const char* name = syscall_table[i];
       id = context_->storage->InternString(name);
diff --git a/src/trace_processor/syscall_tracker.h b/src/trace_processor/syscall_tracker.h
index 1dba57d..88d67b4 100644
--- a/src/trace_processor/syscall_tracker.h
+++ b/src/trace_processor/syscall_tracker.h
@@ -58,7 +58,8 @@
     StringId name = SyscallNumberToStringId(syscall_num);
     if (!name.is_null()) {
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      context_->slice_tracker->Begin(ts, track_id, 0 /* cat */, name);
+      context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
+                                     name);
     }
   }
 
@@ -66,7 +67,7 @@
     StringId name = SyscallNumberToStringId(syscall_num);
     if (!name.is_null()) {
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      context_->slice_tracker->End(ts, track_id, 0 /* cat */, name);
+      context_->slice_tracker->End(ts, track_id, kNullStringId /* cat */, name);
     }
   }
 
@@ -77,14 +78,14 @@
 
   inline StringId SyscallNumberToStringId(uint32_t syscall_num) {
     if (syscall_num > kMaxSyscalls)
-      return 0;
+      return kNullStringId;
     // We see two write sys calls around each userspace slice that is going via
     // trace_marker, this violates the assumption that userspace slices are
     // perfectly nested. For the moment ignore all write sys calls.
     // TODO(hjd): Remove this limitation.
     StringId id = arch_syscall_to_string_id_[syscall_num];
     if (id == sys_write_string_id_)
-      return 0;
+      return kNullStringId;
     return id;
   }
 
diff --git a/src/trace_processor/syscall_tracker_unittest.cc b/src/trace_processor/syscall_tracker_unittest.cc
index f7a2eef..61124d1 100644
--- a/src/trace_processor/syscall_tracker_unittest.cc
+++ b/src/trace_processor/syscall_tracker_unittest.cc
@@ -64,8 +64,8 @@
 
 TEST_F(SyscallTrackerTest, ReportUnknownSyscalls) {
   constexpr TrackId track{0u};
-  StringId begin_name = 0;
-  StringId end_name = 0;
+  StringId begin_name = kNullStringId;
+  StringId end_name = kNullStringId;
   EXPECT_CALL(*slice_tracker, Begin(100, track, kNullStringId, _, _))
       .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(base::nullopt)));
   EXPECT_CALL(*slice_tracker, End(110, track, kNullStringId, _, _))
@@ -90,8 +90,8 @@
 
 TEST_F(SyscallTrackerTest, Aarch64) {
   constexpr TrackId track{0u};
-  StringId begin_name = 0;
-  StringId end_name = 0;
+  StringId begin_name = kNullStringId;
+  StringId end_name = kNullStringId;
   EXPECT_CALL(*slice_tracker, Begin(100, track, kNullStringId, _, _))
       .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(base::nullopt)));
   EXPECT_CALL(*slice_tracker, End(110, track, kNullStringId, _, _))
@@ -107,8 +107,8 @@
 
 TEST_F(SyscallTrackerTest, x8664) {
   constexpr TrackId track{0u};
-  StringId begin_name = 0;
-  StringId end_name = 0;
+  StringId begin_name = kNullStringId;
+  StringId end_name = kNullStringId;
   EXPECT_CALL(*slice_tracker, Begin(100, track, kNullStringId, _, _))
       .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(base::nullopt)));
   EXPECT_CALL(*slice_tracker, End(110, track, kNullStringId, _, _))
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 268e419..83628fa 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -33,9 +33,7 @@
 
 source_set("unittests") {
   testonly = true
-  sources = [
-    "macros_unittest.cc",
-  ]
+  sources = [ "macros_unittest.cc" ]
   deps = [
     ":tables",
     "../../../gn:default_deps",
@@ -51,8 +49,6 @@
       "../../../gn:benchmark",
       "../../../gn:default_deps",
     ]
-    sources = [
-      "macros_benchmark.cc",
-    ]
+    sources = [ "macros_benchmark.cc" ]
   }
 }
diff --git a/src/trace_processor/tables/macros_benchmark.cc b/src/trace_processor/tables/macros_benchmark.cc
index 2e7bfbb..c4ed6e3 100644
--- a/src/trace_processor/tables/macros_benchmark.cc
+++ b/src/trace_processor/tables/macros_benchmark.cc
@@ -27,6 +27,7 @@
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                  \
   C(uint32_t, root_sorted, Column::Flag::kSorted)    \
   C(uint32_t, root_non_null)                         \
+  C(uint32_t, root_non_null_2)                       \
   C(base::Optional<uint32_t>, root_nullable)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_ROOT_TEST_TABLE);
@@ -200,6 +201,28 @@
 }
 BENCHMARK(BM_TableFilterRootNonNullEqMatchMany)->Apply(TableFilterArgs);
 
+static void BM_TableFilterRootMultipleNonNull(benchmark::State& state) {
+  StringPool pool;
+  RootTestTable root(&pool, nullptr);
+
+  uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t partitions = size / 512;
+
+  std::minstd_rand0 rnd_engine;
+  for (uint32_t i = 0; i < size; ++i) {
+    RootTestTable::Row row;
+    row.root_non_null = rnd_engine() % partitions;
+    row.root_non_null_2 = rnd_engine() % partitions;
+    root.Insert(row);
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(root.Filter(
+        {root.root_non_null().lt(4), root.root_non_null_2().lt(10)}));
+  }
+}
+BENCHMARK(BM_TableFilterRootMultipleNonNull)->Apply(TableFilterArgs);
+
 static void BM_TableFilterRootNullableEqMatchMany(benchmark::State& state) {
   StringPool pool;
   RootTestTable root(&pool, nullptr);
@@ -336,6 +359,31 @@
 }
 BENCHMARK(BM_TableFilterParentSortedEq)->Apply(TableFilterArgs);
 
+static void BM_TableFilterParentSortedAndOther(benchmark::State& state) {
+  StringPool pool;
+  RootTestTable root(&pool, nullptr);
+
+  uint32_t size = static_cast<uint32_t>(state.range(0));
+
+  for (uint32_t i = 0; i < size; ++i) {
+    // Group the rows into rows of 10. This emulates the behaviour of e.g.
+    // args.
+    RootTestTable::Row row;
+    row.root_sorted = (i / 10) * 10;
+    row.root_non_null = i;
+    root.Insert(row);
+  }
+
+  // We choose to search for the last group as if there is O(n^2), it will
+  // be more easily visible.
+  uint32_t last_group = ((size - 1) / 10) * 10;
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(root.Filter({root.root_sorted().eq(last_group),
+                                          root.root_non_null().eq(size - 1)}));
+  }
+}
+BENCHMARK(BM_TableFilterParentSortedAndOther)->Apply(TableFilterArgs);
+
 static void BM_TableFilterChildSortedEq(benchmark::State& state) {
   StringPool pool;
   RootTestTable root(&pool, nullptr);
diff --git a/src/trace_processor/tables/macros_internal.h b/src/trace_processor/tables/macros_internal.h
index 37c09f1..1533f91 100644
--- a/src/trace_processor/tables/macros_internal.h
+++ b/src/trace_processor/tables/macros_internal.h
@@ -122,6 +122,9 @@
 
 }  // namespace macros_internal
 
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
 // Basic helper macros.
 #define PERFETTO_TP_NOOP(...)
 
@@ -191,7 +194,7 @@
 
 // Defines the member variable in the Table.
 #define PERFETTO_TP_TABLE_MEMBER(type, name, ...) \
-  SparseVector<TypedColumn<type>::StoredType> name##_;
+  SparseVector<TypedColumn<type>::serialized_type> name##_;
 
 // Constructs the column in the Table constructor when flags are specified.
 #define PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN_FLAGS(type, name, flags)          \
@@ -261,6 +264,7 @@
       explicit constexpr DefinedId(uint32_t v) : value(v) {}                  \
                                                                               \
       bool operator==(const DefinedId& o) const { return o.value == value; }  \
+      bool operator<(const DefinedId& o) const { return value < o.value; }    \
                                                                               \
       uint32_t value;                                                         \
     };                                                                        \
@@ -293,7 +297,7 @@
       }                                                                       \
                                                                               \
       bool operator==(const class_name::Row& other) const {                   \
-        return PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_ROW_EQUALS) true;   \
+        return PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_ROW_EQUALS) true;     \
       }                                                                       \
                                                                               \
       /*                                                                      \
@@ -305,6 +309,12 @@
       PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_ROW_DEFINITION)              \
     };                                                                        \
                                                                               \
+    enum class ColumnIndex : uint32_t {                                       \
+      id,                                                                     \
+      type, /* Expands to col1, col2, ... */                                  \
+      PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_NAME_COMMA) kNumCols           \
+    };                                                                        \
+                                                                              \
     class_name(StringPool* pool, parent_class_name* parent)                   \
         : macros_internal::MacroTable(table_name, pool, parent),              \
           parent_(parent) {                                                   \
@@ -360,12 +370,6 @@
     PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_COL_ACCESSOR)              \
                                                                               \
    private:                                                                   \
-    enum class ColumnIndex : uint32_t {                                       \
-      id,                                                                     \
-      type, /* Expands to col1, col2, ... */                                  \
-      PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_NAME_COMMA) kNumCols           \
-    };                                                                        \
-                                                                              \
     parent_class_name* parent_;                                               \
                                                                               \
     /*                                                                        \
diff --git a/src/trace_processor/tables/metadata_tables.h b/src/trace_processor/tables/metadata_tables.h
index 9a2095d..dfcb98b 100644
--- a/src/trace_processor/tables/metadata_tables.h
+++ b/src/trace_processor/tables/metadata_tables.h
@@ -23,6 +23,30 @@
 namespace trace_processor {
 namespace tables {
 
+#define PERFETTO_TP_RAW_TABLE_DEF(NAME, PARENT, C) \
+  NAME(RawTable, "raw")                            \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                \
+  C(int64_t, ts, Column::Flag::kSorted)            \
+  C(StringPool::Id, name)                          \
+  C(uint32_t, cpu)                                 \
+  C(uint32_t, utid)                                \
+  C(uint32_t, arg_set_id)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_RAW_TABLE_DEF);
+
+#define PERFETTO_TP_ARG_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ArgTable, "args")                           \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                \
+  C(uint32_t, arg_set_id, Column::Flag::kSorted)   \
+  C(StringPool::Id, flat_key)                      \
+  C(StringPool::Id, key)                           \
+  C(base::Optional<int64_t>, int_value)            \
+  C(base::Optional<StringPool::Id>, string_value)  \
+  C(base::Optional<double>, real_value)            \
+  C(StringPool::Id, value_type)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ARG_TABLE_DEF);
+
 #define PERFETTO_TP_METADATA_TABLE_DEF(NAME, PARENT, C) \
   NAME(MetadataTable, "metadata")                       \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                     \
@@ -33,6 +57,29 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_METADATA_TABLE_DEF);
 
+#define PERFETTO_TP_THREAD_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ThreadTable, "internal_thread")                \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                   \
+  C(uint32_t, tid)                                    \
+  C(StringPool::Id, name)                             \
+  C(base::Optional<int64_t>, start_ts)                \
+  C(base::Optional<int64_t>, end_ts)                  \
+  C(base::Optional<uint32_t>, upid)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_THREAD_TABLE_DEF);
+
+#define PERFETTO_TP_PROCESS_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ProcessTable, "internal_process")               \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                    \
+  C(uint32_t, pid)                                     \
+  C(StringPool::Id, name)                              \
+  C(base::Optional<int64_t>, start_ts)                 \
+  C(base::Optional<int64_t>, end_ts)                   \
+  C(base::Optional<uint32_t>, parent_upid)             \
+  C(base::Optional<uint32_t>, uid)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_PROCESS_TABLE_DEF);
+
 }  // namespace tables
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index c735997..04dae8d 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -85,6 +85,29 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_PROFILE_ALLOCATION_DEF);
 
+// This will eventually go away, when we also pre-compute the cumulative
+// sizes for native heap profiles.
+#define PERFETTO_TP_EXPERIMENTAL_FLAMEGRAPH_NODES(NAME, PARENT, C)        \
+  NAME(ExperimentalFlamegraphNodesTable, "experimental_flamegraph_nodes") \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                                       \
+  C(int64_t, ts, Column::Flag::kSorted)                                   \
+  C(uint32_t, upid)                                                       \
+  C(StringPool::Id, profile_type)                                         \
+  C(uint32_t, depth)                                                      \
+  C(StringPool::Id, name)                                                 \
+  C(StringPool::Id, map_name)                                             \
+  C(int64_t, count)                                                       \
+  C(int64_t, cumulative_count)                                            \
+  C(int64_t, size)                                                        \
+  C(int64_t, cumulative_size)                                             \
+  C(int64_t, alloc_count)                                                 \
+  C(int64_t, cumulative_alloc_count)                                      \
+  C(int64_t, alloc_size)                                                  \
+  C(int64_t, cumulative_alloc_size)                                       \
+  C(base::Optional<uint32_t>, parent_id)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_FLAMEGRAPH_NODES);
+
 #define PERFETTO_TP_HEAP_GRAPH_OBJECT_DEF(NAME, PARENT, C)  \
   NAME(HeapGraphObjectTable, "heap_graph_object")           \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                         \
@@ -105,7 +128,7 @@
 #define PERFETTO_TP_HEAP_GRAPH_REFERENCE_DEF(NAME, PARENT, C) \
   NAME(HeapGraphReferenceTable, "heap_graph_reference")       \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                           \
-  C(int64_t, reference_set_id)                                \
+  C(int64_t, reference_set_id, Column::Flag::kSorted)         \
   C(int64_t, owner_id)                                        \
   C(int64_t, owned_id)                                        \
   C(StringPool::Id, field_name)                               \
diff --git a/src/trace_processor/tables/slice_tables.h b/src/trace_processor/tables/slice_tables.h
index 3c34ff6..7d041bb 100644
--- a/src/trace_processor/tables/slice_tables.h
+++ b/src/trace_processor/tables/slice_tables.h
@@ -50,11 +50,15 @@
 PERFETTO_TP_TABLE(PERFETTO_TP_INSTANT_TABLE_DEF);
 
 #define PERFETTO_TP_GPU_SLICES_DEF(NAME, PARENT, C) \
-  NAME(GpuSliceTable, "internal_gpu_slice")         \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                 \
-  C(uint32_t, slice_id, Column::kSorted)            \
+  NAME(GpuSliceTable, "gpu_slice")                  \
+  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)            \
   C(base::Optional<int64_t>, context_id)            \
   C(base::Optional<int64_t>, render_target)         \
+  C(StringPool::Id, render_target_name)             \
+  C(base::Optional<int64_t>, render_pass)           \
+  C(StringPool::Id, render_pass_name)               \
+  C(base::Optional<int64_t>, command_buffer)        \
+  C(StringPool::Id, command_buffer_name)            \
   C(base::Optional<uint32_t>, frame_id)             \
   C(base::Optional<uint32_t>, submission_id)        \
   C(base::Optional<uint32_t>, hw_queue_id)
diff --git a/src/trace_processor/thread_table.cc b/src/trace_processor/thread_table.cc
deleted file mode 100644
index 1ead7c5..0000000
--- a/src/trace_processor/thread_table.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2018 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/trace_processor/thread_table.h"
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/sqlite/query_constraints.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-
-using namespace sqlite_utils;
-
-}  // namespace
-
-ThreadTable::ThreadTable(sqlite3*, const TraceStorage* storage)
-    : storage_(storage) {}
-
-void ThreadTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<ThreadTable>(db, storage, "thread");
-}
-
-util::Status ThreadTable::Init(int, const char* const*, Schema* schema) {
-  *schema = Schema(
-      {
-          SqliteTable::Column(Column::kUtid, "utid", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kUpid, "upid", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kName, "name", SqlValue::Type::kString),
-          SqliteTable::Column(Column::kTid, "tid", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kStartTs, "start_ts",
-                              SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kEndTs, "end_ts", SqlValue::Type::kLong),
-      },
-      {Column::kUtid});
-  return util::OkStatus();
-}
-
-std::unique_ptr<SqliteTable::Cursor> ThreadTable::CreateCursor() {
-  return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
-}
-
-int ThreadTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
-  info->estimated_cost = static_cast<uint32_t>(storage_->thread_count());
-
-  // If the query has a constraint on the |utid| field, return a reduced cost
-  // because we can do that filter efficiently.
-  const auto& constraints = qc.constraints();
-  for (const auto& cs : qc.constraints()) {
-    if (cs.column == Column::kUtid) {
-      info->estimated_cost = IsOpEq(constraints.front().op) ? 1 : 10;
-    }
-  }
-  return SQLITE_OK;
-}
-
-ThreadTable::Cursor::Cursor(ThreadTable* table)
-    : SqliteTable::Cursor(table), storage_(table->storage_), table_(table) {}
-
-int ThreadTable::Cursor::Filter(const QueryConstraints& qc,
-                                sqlite3_value** argv,
-                                FilterHistory) {
-  *this = Cursor(table_);
-
-  min_ = 0;
-  max_ = static_cast<uint32_t>(storage_->thread_count());
-  desc_ = false;
-
-  for (size_t j = 0; j < qc.constraints().size(); j++) {
-    const auto& cs = qc.constraints()[j];
-    if (cs.column == Column::kUtid) {
-      UniqueTid constraint_utid =
-          static_cast<UniqueTid>(sqlite3_value_int(argv[j]));
-      // Filter the range of utids that we are interested in, based on the
-      // constraints in the query. Everything between min and max (exclusive)
-      // will be returned.
-      if (IsOpEq(cs.op)) {
-        min_ = constraint_utid;
-        max_ = constraint_utid + 1;
-      } else if (IsOpGe(cs.op) || IsOpGt(cs.op)) {
-        min_ = IsOpGt(cs.op) ? constraint_utid + 1 : constraint_utid;
-      } else if (IsOpLe(cs.op) || IsOpLt(cs.op)) {
-        max_ = IsOpLt(cs.op) ? constraint_utid : constraint_utid + 1;
-      }
-    }
-  }
-
-  for (const auto& ob : qc.order_by()) {
-    if (ob.iColumn == Column::kUtid) {
-      desc_ = ob.desc;
-    }
-  }
-  index_ = 0;
-
-  return SQLITE_OK;
-}
-
-int ThreadTable::Cursor::Column(sqlite3_context* context, int N) {
-  uint32_t current = desc_ ? max_ - index_ - 1 : min_ + index_;
-  const auto& thread = storage_->GetThread(current);
-  switch (N) {
-    case Column::kUtid: {
-      sqlite3_result_int64(context, current);
-      break;
-    }
-    case Column::kUpid: {
-      if (thread.upid.has_value()) {
-        sqlite3_result_int64(context, thread.upid.value());
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    case Column::kName: {
-      const auto& name = storage_->GetString(thread.name_id);
-      sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic);
-      break;
-    }
-    case Column::kTid: {
-      sqlite3_result_int64(context, thread.tid);
-      break;
-    }
-    case Column::kStartTs: {
-      if (thread.start_ns != 0) {
-        sqlite3_result_int64(context, thread.start_ns);
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    case Column::kEndTs: {
-      if (thread.end_ns != 0) {
-        sqlite3_result_int64(context, thread.end_ns);
-      } else {
-        sqlite3_result_null(context);
-      }
-      break;
-    }
-    default: {
-      PERFETTO_FATAL("Unknown column %d", N);
-      break;
-    }
-  }
-  return SQLITE_OK;
-}
-
-int ThreadTable::Cursor::Next() {
-  ++index_;
-  return SQLITE_OK;
-}
-
-int ThreadTable::Cursor::Eof() {
-  return index_ >= (max_ - min_);
-}
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/thread_table.h b/src/trace_processor/thread_table.h
deleted file mode 100644
index 152399f..0000000
--- a/src/trace_processor/thread_table.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2018 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_TRACE_PROCESSOR_THREAD_TABLE_H_
-#define SRC_TRACE_PROCESSOR_THREAD_TABLE_H_
-
-#include <limits>
-#include <memory>
-
-#include "src/trace_processor/sqlite/sqlite_table.h"
-#include "src/trace_processor/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// The implementation of the SQLite table containing each unique process with
-// the metadata for those processes.
-class ThreadTable : public SqliteTable {
- public:
-  enum Column {
-    kUtid = 0,
-    kUpid = 1,
-    kName = 2,
-    kTid = 3,
-    kStartTs = 4,
-    kEndTs = 5,
-  };
-  class Cursor : public SqliteTable::Cursor {
-   public:
-    Cursor(ThreadTable* table);
-
-    // Implementation of Table::Cursor.
-    int Filter(const QueryConstraints&,
-               sqlite3_value**,
-               FilterHistory) override;
-    int Next() override;
-    int Eof() override;
-    int Column(sqlite3_context*, int N) override;
-
-   private:
-    Cursor(Cursor&) = delete;
-    Cursor& operator=(const Cursor&) = delete;
-
-    Cursor(Cursor&&) noexcept = default;
-    Cursor& operator=(Cursor&&) = default;
-
-    UniqueTid min_ = 0;
-    UniqueTid max_ = 0;
-    uint32_t index_ = 0;
-    bool desc_ = false;
-
-    const TraceStorage* storage_ = nullptr;
-    ThreadTable* table_ = nullptr;
-  };
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  ThreadTable(sqlite3*, const TraceStorage*);
-
-  // Table implementation.
-  util::Status Init(int, const char* const*, SqliteTable::Schema*) override;
-  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
- private:
-  const TraceStorage* const storage_;
-};
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_THREAD_TABLE_H_
diff --git a/src/trace_processor/thread_table_unittest.cc b/src/trace_processor/thread_table_unittest.cc
index 03c6647..fda86e1 100644
--- a/src/trace_processor/thread_table_unittest.cc
+++ b/src/trace_processor/thread_table_unittest.cc
@@ -38,6 +38,7 @@
     db_.reset(db);
 
     context_.storage.reset(new TraceStorage());
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.process_tracker.reset(new ProcessTracker(&context_));
     context_.event_tracker.reset(new EventTracker(&context_));
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index 52ec6de..bb5b6a0 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -20,6 +20,7 @@
 #include "src/trace_processor/chunked_trace_reader.h"
 #include "src/trace_processor/clock_tracker.h"
 #include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/global_args_tracker.h"
 #include "src/trace_processor/heap_profile_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
 #include "src/trace_processor/importers/json/json_trace_parser.h"
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h
index 10e6eb4..63e0886 100644
--- a/src/trace_processor/trace_processor_context.h
+++ b/src/trace_processor/trace_processor_context.h
@@ -33,6 +33,7 @@
 class ClockTracker;
 class EventTracker;
 class FtraceModule;
+class GlobalArgsTracker;
 class HeapGraphTracker;
 class HeapProfileTracker;
 class MetadataTracker;
@@ -52,7 +53,6 @@
 
   std::unique_ptr<TraceStorage> storage;
   std::unique_ptr<TrackTracker> track_tracker;
-  std::unique_ptr<ArgsTracker> args_tracker;
   std::unique_ptr<SliceTracker> slice_tracker;
   std::unique_ptr<ProcessTracker> process_tracker;
   std::unique_ptr<EventTracker> event_tracker;
@@ -63,14 +63,20 @@
   std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
   std::unique_ptr<MetadataTracker> metadata_tracker;
 
+  // Keep the global tracker before the args tracker as we access the global
+  // tracker in the destructor of the args tracker.
+  std::unique_ptr<GlobalArgsTracker> global_args_tracker;
+  std::unique_ptr<ArgsTracker> args_tracker;
+
   // These fields are stored as pointers to Destructible objects rather than
   // their actual type (a subclass of Destructible), as the concrete subclass
   // type is only available in the storage_full target. To access these fields,
   // use the GetOrCreate() method on their subclass type,
   // e.g. SyscallTracker::GetOrCreate(context).
-  std::unique_ptr<Destructible> syscall_tracker;  // SyscallTracker
-  std::unique_ptr<Destructible> sched_tracker;    // SchedEventTracker
-  std::unique_ptr<Destructible> systrace_parser;  // SystraceParser
+  std::unique_ptr<Destructible> syscall_tracker;     // SyscallTracker
+  std::unique_ptr<Destructible> sched_tracker;       // SchedEventTracker
+  std::unique_ptr<Destructible> systrace_parser;     // SystraceParser
+  std::unique_ptr<Destructible> heap_graph_tracker;  // HeapGraphTracker
 
   // This will be nullptr in the minimal build (storage_minimal target), and
   // a pointer to the instance of SystraceTraceParser class in the full build
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index c843df7..9f14054 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -23,10 +23,8 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/args_table.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/process_table.h"
-#include "src/trace_processor/raw_table.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/register_additional_modules.h"
 #include "src/trace_processor/sched_slice_table.h"
 #include "src/trace_processor/span_join_operator_table.h"
@@ -34,8 +32,10 @@
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 #include "src/trace_processor/sqlite/sqlite3_str_split.h"
 #include "src/trace_processor/sqlite/sqlite_table.h"
+#include "src/trace_processor/sqlite_experimental_flamegraph_table.h"
+#include "src/trace_processor/sqlite_raw_table.h"
 #include "src/trace_processor/stats_table.h"
-#include "src/trace_processor/thread_table.h"
+#include "src/trace_processor/types/variadic.h"
 #include "src/trace_processor/window_operator_table.h"
 
 #include "src/trace_processor/metrics/metrics.descriptor.h"
@@ -179,18 +179,6 @@
   }
 
   sqlite3_exec(db,
-               "CREATE VIEW gpu_slice AS "
-               "SELECT "
-               "* "
-               "FROM internal_gpu_slice join internal_slice "
-               "ON internal_gpu_slice.slice_id = internal_slice.id;",
-               0, 0, &error);
-  if (error) {
-    PERFETTO_ELOG("Error initializing: %s", error);
-    sqlite3_free(error);
-  }
-
-  sqlite3_exec(db,
                "CREATE VIEW instants AS "
                "SELECT "
                "*, "
@@ -212,6 +200,30 @@
     PERFETTO_ELOG("Error initializing: %s", error);
     sqlite3_free(error);
   }
+
+  sqlite3_exec(db,
+               "CREATE VIEW thread AS "
+               "SELECT "
+               "id as utid, "
+               "* "
+               "FROM internal_thread;",
+               0, 0, &error);
+  if (error) {
+    PERFETTO_ELOG("Error initializing: %s", error);
+    sqlite3_free(error);
+  }
+
+  sqlite3_exec(db,
+               "CREATE VIEW process AS "
+               "SELECT "
+               "id as upid, "
+               "* "
+               "FROM internal_process;",
+               0, 0, &error);
+  if (error) {
+    PERFETTO_ELOG("Error initializing: %s", error);
+    sqlite3_free(error);
+  }
 }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
@@ -348,13 +360,22 @@
       PERFETTO_ELOG("Error initializing RepeatedField");
   }
 }
+
+void EnsureSqliteInitialized() {
+  // sqlite3_initialize isn't actually thread-safe despite being documented
+  // as such; we need to make sure multiple TraceProcessorImpl instances don't
+  // call it concurrently and only gets called once per process, instead.
+  static bool init_once = [] { return sqlite3_initialize() == SQLITE_OK; }();
+  PERFETTO_CHECK(init_once);
+}
+
 }  // namespace
 
 TraceProcessorImpl::TraceProcessorImpl(const Config& cfg)
     : TraceProcessorStorageImpl(cfg) {
   RegisterAdditionalModules(&context_);
   sqlite3* db = nullptr;
-  PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
+  EnsureSqliteInitialized();
   PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
   InitializeSqlite(db);
   CreateBuiltinTables(db);
@@ -369,18 +390,27 @@
 
   SetupMetrics(this, *db_, &sql_metrics_);
 
-  ArgsTable::RegisterTable(*db_, context_.storage.get());
-  ProcessTable::RegisterTable(*db_, context_.storage.get());
-  SchedSliceTable::RegisterTable(*db_, context_.storage.get());
-  SqlStatsTable::RegisterTable(*db_, context_.storage.get());
-  ThreadTable::RegisterTable(*db_, context_.storage.get());
-  SpanJoinOperatorTable::RegisterTable(*db_, context_.storage.get());
-  WindowOperatorTable::RegisterTable(*db_, context_.storage.get());
-  StatsTable::RegisterTable(*db_, context_.storage.get());
-  RawTable::RegisterTable(*db_, context_.storage.get());
+  const TraceStorage* storage = context_.storage.get();
+
+  SchedSliceTable::RegisterTable(*db_, storage);
+  SqlStatsTable::RegisterTable(*db_, storage);
+  StatsTable::RegisterTable(*db_, storage);
+
+  // Operator tables.
+  SpanJoinOperatorTable::RegisterTable(*db_, storage);
+  WindowOperatorTable::RegisterTable(*db_, storage);
+
+  // New style tables but with some custom logic.
+  SqliteExperimentalFlamegraphTable::RegisterTable(*db_, &context_);
+  SqliteRawTable::RegisterTable(*db_, context_.storage.get());
 
   // New style db-backed tables.
-  const TraceStorage* storage = context_.storage.get();
+  DbSqliteTable::RegisterTable(*db_, &storage->arg_table(),
+                               storage->arg_table().table_name());
+  DbSqliteTable::RegisterTable(*db_, &storage->thread_table(),
+                               storage->thread_table().table_name());
+  DbSqliteTable::RegisterTable(*db_, &storage->process_table(),
+                               storage->process_table().table_name());
 
   DbSqliteTable::RegisterTable(*db_, &storage->slice_table(),
                                storage->slice_table().table_name());
@@ -483,6 +513,9 @@
   TraceProcessorStorageImpl::NotifyEndOfFile();
 
   SchedEventTracker::GetOrCreate(&context_)->FlushPendingEvents();
+  context_.metadata_tracker->SetMetadata(
+      metadata::trace_size_bytes,
+      Variadic::Integer(static_cast<int64_t>(bytes_parsed_)));
   BuildBoundsTable(*db_, context_.storage->GetTraceTimestampBoundsNs());
 
   // Create a snapshot of all tables and views created so far. This is so later
@@ -516,7 +549,12 @@
     auto it = ExecuteQuery(query);
     while (it.Next()) {
     }
-    PERFETTO_CHECK(it.Status().ok());
+    // Index deletion can legitimately fail. If one creates an index "i" on a
+    // table "t" but issues the deletion in the order (t, i), the DROP index i
+    // will fail with "no such index" because deleting the table "t"
+    // automatically deletes all associated indexes.
+    if (!it.Status().ok() && tn.first != "index")
+      PERFETTO_FATAL("%s -> %s", query.c_str(), it.Status().c_message());
   }
   return deletion_list.size();
 }
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 5cc06cb..594e13e 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -36,6 +36,7 @@
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/trace_processor/read_trace.h"
 #include "perfetto/trace_processor/trace_processor.h"
+#include "src/trace_processor/metrics/custom_options.descriptor.h"
 #include "src/trace_processor/metrics/metrics.descriptor.h"
 #include "src/trace_processor/proto_to_json.h"
 
@@ -290,15 +291,20 @@
   }
 };
 
+// This function returns an indentifier for a metric suitable for use
+// as an SQL table name (i.e. containing no forward or backward slashes).
+std::string BaseName(std::string metric_path) {
+  std::replace(metric_path.begin(), metric_path.end(), '\\', '/');
+  auto slash_idx = metric_path.rfind('/');
+  return slash_idx == std::string::npos ? metric_path
+                                        : metric_path.substr(slash_idx + 1);
+}
+
 util::Status RegisterMetric(const std::string& register_metric) {
   std::string sql;
   base::ReadFile(register_metric, &sql);
 
-  std::string path = "shell/";
-  auto slash_idx = register_metric.rfind('/');
-  path += slash_idx == std::string::npos
-              ? register_metric
-              : register_metric.substr(slash_idx + 1);
+  std::string path = "shell/" + BaseName(register_metric);
 
   return g_tp->RegisterMetric(path, sql);
 }
@@ -317,16 +323,12 @@
   ErrorPrinter printer;
   google::protobuf::io::Tokenizer tokenizer(&stream, &printer);
 
-  auto* proto = desc_set.add_file();
+  auto* file_desc = desc_set.add_file();
   google::protobuf::compiler::Parser parser;
-  parser.Parse(&tokenizer, proto);
+  parser.Parse(&tokenizer, file_desc);
 
-  auto basename_idx = extend_metrics_proto.rfind('/');
-  auto basename = basename_idx == std::string::npos
-                      ? extend_metrics_proto
-                      : extend_metrics_proto.substr(basename_idx + 1);
-  proto->set_name(basename);
-  pool->BuildFile(*proto);
+  file_desc->set_name(BaseName(extend_metrics_proto));
+  pool->BuildFile(*file_desc);
 
   std::vector<uint8_t> metric_proto;
   metric_proto.resize(static_cast<size_t>(desc_set.ByteSize()));
@@ -375,7 +377,13 @@
       break;
     }
     case OutputFormat::kJson: {
-      auto out = proto_to_json::MessageToJson(*metrics) + "\n";
+      // We need to instantiate field options from dynamic message factory
+      // because otherwise it cannot parse our custom extensions.
+      const google::protobuf::Message* field_options_prototype =
+          factory.GetPrototype(
+              pool.FindMessageTypeByName("google.protobuf.FieldOptions"));
+      auto out = proto_to_json::MessageToJsonWithAnnotations(
+          *metrics, field_options_prototype, 0);
       fwrite(out.c_str(), sizeof(char), out.size(), stdout);
       break;
     }
@@ -654,30 +662,51 @@
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 void PrintUsage(char** argv) {
-  PERFETTO_ELOG(
-      "Interactive trace processor shell.\n"
-      "Usage: %s [-q query_file] trace_file.pb",
-      argv[0]);
+  PERFETTO_ELOG(R"(
+Interactive trace processor shell.
+Usage: %s [OPTIONS] trace_file.pb
+
+Options:
+ -q, --query-file FILE                Read and execute an SQL query from a file.
+                                      If used with --run-metrics, the query is
+                                      executed after the selected metrics and
+                                      the metrics output is suppressed.
+ --run-metrics x,y,z                  Runs a comma separated list of metrics and
+                                      prints the result as a TraceMetrics proto
+                                      to stdout. The specified can either be
+                                      in-built metrics or SQL/proto files of
+                                      extension metrics.
+ --metrics-output [binary|text|json]  Allows the output of --run-metrics to be
+                                      specified in either proto binary, proto
+                                      text format or JSON format (default: proto
+                                      text).)",
+                argv[0]);
 }
 
 CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
   CommandLineOptions command_line_options;
 
-  if (argc == 2) {
-    command_line_options.trace_file_path = argv[1];
-    command_line_options.launch_shell = true;
-  } else if (argc == 4) {
-    if (strcmp(argv[1], "-q") != 0) {
-      PrintUsage(argv);
-      exit(1);
-    }
-    command_line_options.query_file_path = argv[2];
-    command_line_options.trace_file_path = argv[3];
-  } else {
+  if (argc < 2 || argc % 2 == 1) {
     PrintUsage(argv);
     exit(1);
   }
 
+  for (int i = 1; i < argc - 1; i += 2) {
+    if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--query-file") == 0) {
+      command_line_options.query_file_path = argv[i + 1];
+    } else if (strcmp(argv[i], "--run-metrics") == 0) {
+      command_line_options.metric_names = argv[i + 1];
+    } else if (strcmp(argv[i], "--metrics-output") == 0) {
+      command_line_options.metric_output = argv[i + 1];
+    } else {
+      PrintUsage(argv);
+      exit(1);
+    }
+  }
+  command_line_options.trace_file_path = argv[argc - 1];
+  command_line_options.launch_shell =
+      command_line_options.metric_names.empty() &&
+      command_line_options.query_file_path.empty();
   return command_line_options;
 }
 
@@ -895,6 +924,16 @@
 
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 
+void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
+                                    const void* data,
+                                    int size) {
+  google::protobuf::FileDescriptorSet desc_set;
+  desc_set.ParseFromArray(data, size);
+  for (const auto& desc : desc_set.file()) {
+    pool.BuildFile(desc);
+  }
+}
+
 int TraceProcessorMain(int argc, char** argv) {
   CommandLineOptions options = ParseCommandLineOptions(argc, argv);
 
@@ -970,13 +1009,14 @@
   auto t_run_start = base::GetWallTimeNs();
 
   // Descriptor pool used for printing output as textproto.
-  google::protobuf::DescriptorPool pool;
-  google::protobuf::FileDescriptorSet root_desc_set;
-  root_desc_set.ParseFromArray(kMetricsDescriptor.data(),
-                               kMetricsDescriptor.size());
-  for (const auto& desc : root_desc_set.file()) {
-    pool.BuildFile(desc);
-  }
+  // Building on top of generated pool so default protos in
+  // google.protobuf.descriptor.proto are available.
+  google::protobuf::DescriptorPool pool(
+      google::protobuf::DescriptorPool::generated_pool());
+  ExtendPoolWithBinaryDescriptor(pool, kMetricsDescriptor.data(),
+                                 kMetricsDescriptor.size());
+  ExtendPoolWithBinaryDescriptor(pool, kCustomOptionsDescriptor.data(),
+                                 kCustomOptionsDescriptor.size());
 
   if (!options.metric_extra.empty()) {
     util::Status status = RegisterExtraMetrics(options.metric_extra, "");
@@ -1028,11 +1068,7 @@
         return 1;
       }
 
-      auto slash_idx = no_ext_name.rfind('/');
-      std::string basename = slash_idx == std::string::npos
-                                 ? no_ext_name
-                                 : no_ext_name.substr(slash_idx + 1);
-      metrics[i] = basename;
+      metrics[i] = BaseName(no_ext_name);
     }
 
     OutputFormat format;
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 0ad11ee..a8d8dee 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -48,6 +48,7 @@
   context_.clock_tracker.reset(new ClockTracker(&context_));
   context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
   context_.metadata_tracker.reset(new MetadataTracker(&context_));
+  context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
 
   context_.modules.emplace_back(new FtraceModule());
   // Ftrace module is special, because it has one extra method for parsing
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/trace_storage.cc
index 4d96c7e..b8d79f4 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/trace_storage.cc
@@ -43,7 +43,7 @@
 void DbTableMaybeUpdateMinMax(const TypedColumn<int64_t>& column,
                               int64_t* min_value,
                               int64_t* max_value) {
-  if (column.row_map().size() == 0)
+  if (column.row_map().empty())
     return;
 
   SqlValue col_min = *column.Min();
@@ -77,11 +77,19 @@
   return map.ref();
 }
 
-TraceStorage::TraceStorage(const Config& config)
-    : string_pool_(config.string_pool_block_size_bytes) {
+TraceStorage::TraceStorage(const Config&) {
   // Upid/utid 0 is reserved for idle processes/threads.
-  unique_processes_.emplace_back(0);
-  unique_threads_.emplace_back(0);
+  tables::ThreadTable::Row thread_row;
+  thread_row.tid = 0;
+  thread_table_.Insert(thread_row);
+
+  tables::ProcessTable::Row process_row;
+  process_row.pid = 0;
+  process_table_.Insert(process_row);
+
+  for (uint32_t i = 0; i < variadic_type_ids_.size(); ++i) {
+    variadic_type_ids_[i] = InternString(Variadic::kTypeNames[i]);
+  }
 }
 
 TraceStorage::~TraceStorage() {}
@@ -131,15 +139,16 @@
   int64_t end_ns = std::numeric_limits<int64_t>::min();
   MaybeUpdateMinMax(slices_.start_ns().begin(), slices_.start_ns().end(),
                     &start_ns, &end_ns);
-  MaybeUpdateMinMax(raw_events_.timestamps().begin(),
-                    raw_events_.timestamps().end(), &start_ns, &end_ns);
 
+  DbTableMaybeUpdateMinMax(raw_table_.ts(), &start_ns, &end_ns);
   DbTableMaybeUpdateMinMax(counter_table_.ts(), &start_ns, &end_ns);
   DbTableMaybeUpdateMinMax(slice_table_.ts(), &start_ns, &end_ns);
   DbTableMaybeUpdateMinMax(heap_profile_allocation_table_.ts(), &start_ns,
                            &end_ns);
   DbTableMaybeUpdateMinMax(instant_table_.ts(), &start_ns, &end_ns);
   DbTableMaybeUpdateMinMax(android_log_table_.ts(), &start_ns, &end_ns);
+  DbTableMaybeUpdateMinMax(heap_graph_object_table_.graph_sample_ts(),
+                           &start_ns, &end_ns);
 
   if (start_ns == std::numeric_limits<int64_t>::max()) {
     return std::make_pair(0, 0);
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 83a609b..67c6358 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -42,7 +42,7 @@
 #include "src/trace_processor/tables/profiler_tables.h"
 #include "src/trace_processor/tables/slice_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
-#include "src/trace_processor/variadic.h"
+#include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -58,20 +58,7 @@
 
 // StringId is an offset into |string_pool_|.
 using StringId = StringPool::Id;
-static const StringId kNullStringId = StringId(0);
-
-// Identifiers for all the tables in the database.
-enum class TableId : uint8_t {
-  kInvalid = 0,
-  kCounterValues = 1,
-  kRawEvents = 2,
-  kInstants = 3,
-  kSched = 4,
-  kNestableSlices = 5,
-  kMetadataTable = 6,
-  kTrack = 7,
-  kVulkanMemoryAllocation = 8,
-};
+static const StringId kNullStringId = StringId::Null();
 
 using ArgSetId = uint32_t;
 static const ArgSetId kInvalidArgSetId = 0;
@@ -86,8 +73,18 @@
 
 using MappingId = tables::StackProfileMappingTable::Id;
 
+using FrameId = tables::StackProfileFrameTable::Id;
+
+using SymbolId = tables::SymbolTable::Id;
+
+using CallsiteId = tables::StackProfileCallsiteTable::Id;
+
 using MetadataId = tables::MetadataTable::Id;
 
+using RawId = tables::RawTable::Id;
+
+using VulkanAllocId = tables::VulkanMemoryAllocationsTable::Id;
+
 // TODO(lalitm): this is a temporary hack while migrating the counters table and
 // will be removed when the migration is complete.
 static const TrackId kInvalidTrackId =
@@ -116,117 +113,6 @@
 
   virtual ~TraceStorage();
 
-  // Information about a unique process seen in a trace.
-  struct Process {
-    explicit Process(uint32_t p) : pid(p) {}
-    int64_t start_ns = 0;
-    int64_t end_ns = 0;
-    StringId name_id = 0;
-    uint32_t pid = 0;
-    base::Optional<UniquePid> parent_upid;
-    base::Optional<uint32_t> uid;
-  };
-
-  // Information about a unique thread seen in a trace.
-  struct Thread {
-    explicit Thread(uint32_t t) : tid(t) {}
-    int64_t start_ns = 0;
-    int64_t end_ns = 0;
-    StringId name_id = 0;
-    base::Optional<UniquePid> upid;
-    uint32_t tid = 0;
-  };
-
-  // Generic key value storage which can be referenced by other tables.
-  class Args {
-   public:
-    struct Arg {
-      StringId flat_key = 0;
-      StringId key = 0;
-      Variadic value = Variadic::Integer(0);
-
-      TableId table;
-      uint32_t row;
-    };
-
-    struct ArgHasher {
-      uint64_t operator()(const Arg& arg) const noexcept {
-        base::Hash hash;
-        hash.Update(arg.key);
-        // We don't hash arg.flat_key because it's a subsequence of arg.key.
-        switch (arg.value.type) {
-          case Variadic::Type::kInt:
-            hash.Update(arg.value.int_value);
-            break;
-          case Variadic::Type::kUint:
-            hash.Update(arg.value.uint_value);
-            break;
-          case Variadic::Type::kString:
-            hash.Update(arg.value.string_value);
-            break;
-          case Variadic::Type::kReal:
-            hash.Update(arg.value.real_value);
-            break;
-          case Variadic::Type::kPointer:
-            hash.Update(arg.value.pointer_value);
-            break;
-          case Variadic::Type::kBool:
-            hash.Update(arg.value.bool_value);
-            break;
-          case Variadic::Type::kJson:
-            hash.Update(arg.value.json_value);
-            break;
-        }
-        return hash.digest();
-      }
-    };
-
-    const std::deque<ArgSetId>& set_ids() const { return set_ids_; }
-    const std::deque<StringId>& flat_keys() const { return flat_keys_; }
-    const std::deque<StringId>& keys() const { return keys_; }
-    const std::deque<Variadic>& arg_values() const { return arg_values_; }
-    uint32_t args_count() const {
-      return static_cast<uint32_t>(set_ids_.size());
-    }
-
-    ArgSetId AddArgSet(const std::vector<Arg>& args,
-                       uint32_t begin,
-                       uint32_t end) {
-      base::Hash hash;
-      for (uint32_t i = begin; i < end; i++) {
-        hash.Update(ArgHasher()(args[i]));
-      }
-
-      ArgSetHash digest = hash.digest();
-      auto it = arg_row_for_hash_.find(digest);
-      if (it != arg_row_for_hash_.end()) {
-        return set_ids_[it->second];
-      }
-
-      // The +1 ensures that nothing has an id == kInvalidArgSetId == 0.
-      ArgSetId id = static_cast<uint32_t>(arg_row_for_hash_.size()) + 1;
-      arg_row_for_hash_.emplace(digest, args_count());
-      for (uint32_t i = begin; i < end; i++) {
-        const auto& arg = args[i];
-        set_ids_.emplace_back(id);
-        flat_keys_.emplace_back(arg.flat_key);
-        keys_.emplace_back(arg.key);
-        arg_values_.emplace_back(arg.value);
-      }
-      return id;
-    }
-
-   private:
-    using ArgSetHash = uint64_t;
-
-    std::deque<ArgSetId> set_ids_;
-    std::deque<StringId> flat_keys_;
-    std::deque<StringId> keys_;
-    std::deque<Variadic> arg_values_;
-
-    std::unordered_map<ArgSetHash, uint32_t> arg_row_for_hash_;
-  };
-
   class Slices {
    public:
     inline size_t AddSlice(uint32_t cpu,
@@ -439,42 +325,6 @@
     std::deque<int64_t> times_ended_;
   };
 
-  class RawEvents {
-   public:
-    inline uint32_t AddRawEvent(int64_t timestamp,
-                                StringId name_id,
-                                uint32_t cpu,
-                                UniqueTid utid) {
-      timestamps_.emplace_back(timestamp);
-      name_ids_.emplace_back(name_id);
-      cpus_.emplace_back(cpu);
-      utids_.emplace_back(utid);
-      arg_set_ids_.emplace_back(kInvalidArgSetId);
-      return static_cast<uint32_t>(raw_event_count() - 1);
-    }
-
-    void set_arg_set_id(uint32_t row, ArgSetId id) { arg_set_ids_[row] = id; }
-
-    size_t raw_event_count() const { return timestamps_.size(); }
-
-    const std::deque<int64_t>& timestamps() const { return timestamps_; }
-
-    const std::deque<StringId>& name_ids() const { return name_ids_; }
-
-    const std::deque<uint32_t>& cpus() const { return cpus_; }
-
-    const std::deque<UniqueTid>& utids() const { return utids_; }
-
-    const std::deque<ArgSetId>& arg_set_ids() const { return arg_set_ids_; }
-
-   private:
-    std::deque<int64_t> timestamps_;
-    std::deque<StringId> name_ids_;
-    std::deque<uint32_t> cpus_;
-    std::deque<UniqueTid> utids_;
-    std::deque<ArgSetId> arg_set_ids_;
-  };
-
   struct Stats {
     using IndexMap = std::map<int, int64_t>;
     int64_t value = 0;
@@ -482,16 +332,6 @@
   };
   using StatsMap = std::array<Stats, stats::kNumKeys>;
 
-  UniqueTid AddEmptyThread(uint32_t tid) {
-    unique_threads_.emplace_back(tid);
-    return static_cast<UniqueTid>(unique_threads_.size() - 1);
-  }
-
-  UniquePid AddEmptyProcess(uint32_t pid) {
-    unique_processes_.emplace_back(pid);
-    return static_cast<UniquePid>(unique_processes_.size() - 1);
-  }
-
   // Return an unqiue identifier for the contents of each string.
   // The string is copied internally and can be destroyed after this called.
   // Virtual for testing.
@@ -499,16 +339,6 @@
     return string_pool_.InternString(str);
   }
 
-  Process* GetMutableProcess(UniquePid upid) {
-    PERFETTO_DCHECK(upid < unique_processes_.size());
-    return &unique_processes_[upid];
-  }
-
-  Thread* GetMutableThread(UniqueTid utid) {
-    PERFETTO_DCHECK(utid < unique_threads_.size());
-    return &unique_threads_[utid];
-  }
-
   // Example usage: SetStats(stats::android_log_num_failed, 42);
   void SetStats(size_t key, int64_t value) {
     PERFETTO_DCHECK(key < stats::kNumKeys);
@@ -582,17 +412,11 @@
     return string_pool_.Get(id);
   }
 
-  const Process& GetProcess(UniquePid upid) const {
-    PERFETTO_DCHECK(upid < unique_processes_.size());
-    return unique_processes_[upid];
-  }
+  const tables::ThreadTable& thread_table() const { return thread_table_; }
+  tables::ThreadTable* mutable_thread_table() { return &thread_table_; }
 
-  // Virtual for testing.
-  virtual const Thread& GetThread(UniqueTid utid) const {
-    // Allow utid == 0 for idle thread retrieval.
-    PERFETTO_DCHECK(utid < unique_threads_.size());
-    return unique_threads_[utid];
-  }
+  const tables::ProcessTable& process_table() const { return process_table_; }
+  tables::ProcessTable* mutable_process_table() { return &process_table_; }
 
   const tables::TrackTable& track_table() const { return track_table_; }
   tables::TrackTable* mutable_track_table() { return &track_table_; }
@@ -704,11 +528,11 @@
   }
   tables::MetadataTable* mutable_metadata_table() { return &metadata_table_; }
 
-  const Args& args() const { return args_; }
-  Args* mutable_args() { return &args_; }
+  const tables::ArgTable& arg_table() const { return arg_table_; }
+  tables::ArgTable* mutable_arg_table() { return &arg_table_; }
 
-  const RawEvents& raw_events() const { return raw_events_; }
-  RawEvents* mutable_raw_events() { return &raw_events_; }
+  const tables::RawTable& raw_table() const { return raw_table_; }
+  tables::RawTable* mutable_raw_table() { return &raw_table_; }
 
   const tables::StackProfileMappingTable& stack_profile_mapping_table() const {
     return stack_profile_mapping_table_;
@@ -739,6 +563,7 @@
   tables::HeapProfileAllocationTable* mutable_heap_profile_allocation_table() {
     return &heap_profile_allocation_table_;
   }
+
   const tables::CpuProfileStackSampleTable& cpu_profile_stack_sample_table()
       const {
     return cpu_profile_stack_sample_table_;
@@ -783,14 +608,7 @@
   }
 
   const StringPool& string_pool() const { return string_pool_; }
-
-  // |unique_processes_| always contains at least 1 element because the 0th ID
-  // is reserved to indicate an invalid process.
-  size_t process_count() const { return unique_processes_.size(); }
-
-  // |unique_threads_| always contains at least 1 element because the 0th ID
-  // is reserved to indicate an invalid thread.
-  size_t thread_count() const { return unique_threads_.size(); }
+  StringPool* mutable_string_pool() { return &string_pool_; }
 
   // Number of interned strings in the pool. Includes the empty string w/ ID=0.
   size_t string_count() const { return string_pool_.size(); }
@@ -800,7 +618,8 @@
   std::pair<int64_t, int64_t> GetTraceTimestampBoundsNs() const;
 
   // TODO(lalitm): remove this when we have a better home.
-  std::vector<int64_t> FindMappingRow(StringId name, StringId build_id) const {
+  std::vector<MappingId> FindMappingRow(StringId name,
+                                        StringId build_id) const {
     auto it = stack_profile_mapping_index_.find(std::make_pair(name, build_id));
     if (it == stack_profile_mapping_index_.end())
       return {};
@@ -808,13 +627,14 @@
   }
 
   // TODO(lalitm): remove this when we have a better home.
-  void InsertMappingRow(StringId name, StringId build_id, uint32_t row) {
+  void InsertMappingId(StringId name, StringId build_id, MappingId row) {
     auto pair = std::make_pair(name, build_id);
     stack_profile_mapping_index_[pair].emplace_back(row);
   }
 
   // TODO(lalitm): remove this when we have a better home.
-  std::vector<int64_t> FindFrameRow(size_t mapping_row, uint64_t rel_pc) const {
+  std::vector<FrameId> FindFrameIds(MappingId mapping_row,
+                                    uint64_t rel_pc) const {
     auto it =
         stack_profile_frame_index_.find(std::make_pair(mapping_row, rel_pc));
     if (it == stack_profile_frame_index_.end())
@@ -823,11 +643,48 @@
   }
 
   // TODO(lalitm): remove this when we have a better home.
-  void InsertFrameRow(size_t mapping_row, uint64_t rel_pc, uint32_t row) {
+  void InsertFrameRow(MappingId mapping_row, uint64_t rel_pc, FrameId row) {
     auto pair = std::make_pair(mapping_row, rel_pc);
     stack_profile_frame_index_[pair].emplace_back(row);
   }
 
+  Variadic GetArgValue(uint32_t row) const {
+    Variadic v;
+    v.type = *GetVariadicTypeForId(arg_table_.value_type()[row]);
+
+    // Force initialization of union to stop GCC complaining.
+    v.int_value = 0;
+
+    switch (v.type) {
+      case Variadic::Type::kBool:
+        v.bool_value = static_cast<bool>(*arg_table_.int_value()[row]);
+        break;
+      case Variadic::Type::kInt:
+        v.int_value = *arg_table_.int_value()[row];
+        break;
+      case Variadic::Type::kUint:
+        v.uint_value = static_cast<uint64_t>(*arg_table_.int_value()[row]);
+        break;
+      case Variadic::Type::kString:
+        v.string_value = arg_table_.string_value()[row];
+        break;
+      case Variadic::Type::kPointer:
+        v.pointer_value = static_cast<uint64_t>(*arg_table_.int_value()[row]);
+        break;
+      case Variadic::Type::kReal:
+        v.real_value = *arg_table_.real_value()[row];
+        break;
+      case Variadic::Type::kJson:
+        v.json_value = arg_table_.string_value()[row];
+        break;
+    }
+    return v;
+  }
+
+  StringId GetIdForVariadicType(Variadic::Type type) const {
+    return variadic_type_ids_[type];
+  }
+
  private:
   using StringHash = uint64_t;
 
@@ -837,13 +694,23 @@
   TraceStorage(TraceStorage&&) = delete;
   TraceStorage& operator=(TraceStorage&&) = delete;
 
-  // TODO(lalitm): remove this when we find a better home for this.
-  using MappingKey = std::pair<StringId /* name */, StringId /* build id */>;
-  std::map<MappingKey, std::vector<int64_t>> stack_profile_mapping_index_;
+  base::Optional<Variadic::Type> GetVariadicTypeForId(StringId id) const {
+    auto it =
+        std::find(variadic_type_ids_.begin(), variadic_type_ids_.end(), id);
+    if (it == variadic_type_ids_.end())
+      return base::nullopt;
+
+    int64_t idx = std::distance(variadic_type_ids_.begin(), it);
+    return static_cast<Variadic::Type>(idx);
+  }
 
   // TODO(lalitm): remove this when we find a better home for this.
-  using FrameKey = std::pair<size_t /* mapping row */, uint64_t /* rel_pc */>;
-  std::map<MappingKey, std::vector<int64_t>> stack_profile_frame_index_;
+  using MappingKey = std::pair<StringId /* name */, StringId /* build id */>;
+  std::map<MappingKey, std::vector<MappingId>> stack_profile_mapping_index_;
+
+  // TODO(lalitm): remove this when we find a better home for this.
+  using FrameKey = std::pair<MappingId, uint64_t /* rel_pc */>;
+  std::map<FrameKey, std::vector<FrameId>> stack_profile_frame_index_;
 
   // One entry for each unique string in the trace.
   StringPool string_pool_;
@@ -881,15 +748,11 @@
   Slices slices_;
 
   // Args for all other tables.
-  Args args_;
+  tables::ArgTable arg_table_{&string_pool_, nullptr};
 
-  // One entry for each UniquePid, with UniquePid as the index.
-  // Never hold on to pointers to Process, as vector resize will
-  // invalidate them.
-  std::vector<Process> unique_processes_;
-
-  // One entry for each UniqueTid, with UniqueTid as the index.
-  std::deque<Thread> unique_threads_;
+  // Information about all the threads and processes in the trace.
+  tables::ThreadTable thread_table_{&string_pool_, nullptr};
+  tables::ProcessTable process_table_{&string_pool_, nullptr};
 
   // Slices coming from userspace events (e.g. Chromium TRACE_EVENT macros).
   tables::SliceTable slice_table_{&string_pool_, nullptr};
@@ -903,7 +766,7 @@
 
   // Additional attributes for gpu track slices (sub-type of
   // NestableSlices).
-  tables::GpuSliceTable gpu_slice_table_{&string_pool_, nullptr};
+  tables::GpuSliceTable gpu_slice_table_{&string_pool_, &slice_table_};
 
   // The values from the Counter events from the trace. This includes CPU
   // frequency events as well systrace trace_marker counter events.
@@ -920,7 +783,7 @@
   // the timestamp and the pid. The args for the raw event will be in the
   // args table. This table can be used to generate a text version of the
   // trace.
-  RawEvents raw_events_;
+  tables::RawTable raw_table_{&string_pool_, nullptr};
   tables::AndroidLogTable android_log_table_{&string_pool_, nullptr};
 
   tables::StackProfileMappingTable stack_profile_mapping_table_{&string_pool_,
@@ -942,6 +805,10 @@
 
   tables::VulkanMemoryAllocationsTable vulkan_memory_allocations_table_{
       &string_pool_, nullptr};
+
+  // The below array allow us to map between enums and their string
+  // representations.
+  std::array<StringId, Variadic::kMaxType + 1> variadic_type_ids_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/track_tracker.cc b/src/trace_processor/track_tracker.cc
index 944e674..0ff0bd3 100644
--- a/src/trace_processor/track_tracker.cc
+++ b/src/trace_processor/track_tracker.cc
@@ -17,6 +17,7 @@
 #include "src/trace_processor/track_tracker.h"
 
 #include "src/trace_processor/args_tracker.h"
+#include "src/trace_processor/process_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -30,6 +31,7 @@
       source_id_is_process_scoped_key_(
           context->storage->InternString("source_id_is_process_scoped")),
       source_scope_key_(context->storage->InternString("source_scope")),
+      parent_track_id_key_(context->storage->InternString("parent_track_id")),
       fuchsia_source_(context->storage->InternString("fuchsia")),
       chrome_source_(context->storage->InternString("chrome")),
       android_source_(context->storage->InternString("android")),
@@ -50,6 +52,18 @@
   return id;
 }
 
+TrackId TrackTracker::InternProcessTrack(UniquePid upid) {
+  auto it = process_tracks_.find(upid);
+  if (it != process_tracks_.end())
+    return it->second;
+
+  tables::ProcessTrackTable::Row row;
+  row.upid = upid;
+  auto id = context_->storage->mutable_process_track_table()->Insert(row);
+  process_tracks_[upid] = id;
+  return id;
+}
+
 TrackId TrackTracker::InternFuchsiaAsyncTrack(StringId name,
                                               int64_t correlation_id) {
   auto it = fuchsia_async_tracks_.find(correlation_id);
@@ -60,15 +74,15 @@
   auto id = context_->storage->mutable_track_table()->Insert(row);
   fuchsia_async_tracks_[correlation_id] = id;
 
-  ArgsTracker::BoundInserter inserter(context_->args_tracker.get(),
-                                      TableId::kTrack, id.value);
-  inserter.AddArg(source_key_, Variadic::String(fuchsia_source_));
-  inserter.AddArg(source_id_key_, Variadic::Integer(correlation_id));
+  context_->args_tracker->AddArgsTo(id)
+      .AddArg(source_key_, Variadic::String(fuchsia_source_))
+      .AddArg(source_id_key_, Variadic::Integer(correlation_id));
+
   return id;
 }
 
 TrackId TrackTracker::InternGpuTrack(const tables::GpuTrackTable::Row& row) {
-  GpuTrackTuple tuple{row.name.id, row.scope, row.context_id.value_or(0)};
+  GpuTrackTuple tuple{row.name, row.scope, row.context_id.value_or(0)};
 
   auto it = gpu_tracks_.find(tuple);
   if (it != gpu_tracks_.end())
@@ -102,13 +116,13 @@
   TrackId id = context_->storage->mutable_process_track_table()->Insert(track);
   chrome_tracks_[tuple] = id;
 
-  ArgsTracker::BoundInserter inserter(context_->args_tracker.get(),
-                                      TableId::kTrack, id.value);
-  inserter.AddArg(source_key_, Variadic::String(chrome_source_));
-  inserter.AddArg(source_id_key_, Variadic::Integer(source_id));
-  inserter.AddArg(source_id_is_process_scoped_key_,
-                  Variadic::Boolean(source_id_is_process_scoped));
-  inserter.AddArg(source_scope_key_, Variadic::String(source_scope));
+  context_->args_tracker->AddArgsTo(id)
+      .AddArg(source_key_, Variadic::String(chrome_source_))
+      .AddArg(source_id_key_, Variadic::Integer(source_id))
+      .AddArg(source_id_is_process_scoped_key_,
+              Variadic::Boolean(source_id_is_process_scoped))
+      .AddArg(source_scope_key_, Variadic::String(source_scope));
+
   return id;
 }
 
@@ -126,10 +140,10 @@
   auto id = context_->storage->mutable_process_track_table()->Insert(row);
   android_async_tracks_[tuple] = id;
 
-  ArgsTracker::BoundInserter inserter(context_->args_tracker.get(),
-                                      TableId::kTrack, id.value);
-  inserter.AddArg(source_key_, Variadic::String(android_source_));
-  inserter.AddArg(source_id_key_, Variadic::Integer(cookie));
+  context_->args_tracker->AddArgsTo(id)
+      .AddArg(source_key_, Variadic::String(android_source_))
+      .AddArg(source_id_key_, Variadic::Integer(cookie));
+
   return id;
 }
 
@@ -143,8 +157,9 @@
   auto id = context_->storage->mutable_process_track_table()->Insert(row);
   chrome_process_instant_tracks_[upid] = id;
 
-  context_->args_tracker->AddArg(TableId::kTrack, id.value, source_key_,
-                                 source_key_, Variadic::String(chrome_source_));
+  context_->args_tracker->AddArgsTo(id).AddArg(
+      source_key_, Variadic::String(chrome_source_));
+
   return id;
 }
 
@@ -153,157 +168,236 @@
     chrome_global_instant_track_id_ =
         context_->storage->mutable_track_table()->Insert({});
 
-    context_->args_tracker->AddArg(
-        TableId::kTrack, chrome_global_instant_track_id_->value, source_key_,
-        source_key_, Variadic::String(chrome_source_));
+    context_->args_tracker->AddArgsTo(*chrome_global_instant_track_id_)
+        .AddArg(source_key_, Variadic::String(chrome_source_));
   }
   return *chrome_global_instant_track_id_;
 }
 
-TrackId TrackTracker::UpdateDescriptorTrack(uint64_t uuid,
-                                            StringId name,
-                                            base::Optional<UniquePid> upid,
-                                            base::Optional<UniqueTid> utid) {
-  auto it = descriptor_tracks_.find(uuid);
-  if (it != descriptor_tracks_.end()) {
-    // Update existing track for |uuid|.
-    TrackId track_id = it->second;
-    if (name != kNullStringId) {
-      auto* track = context_->storage->mutable_track_table();
-      auto row = *track->id().IndexOf(track_id);
-      track->mutable_name()->Set(row, name);
-    }
+void TrackTracker::ReserveDescriptorProcessTrack(uint64_t uuid,
+                                                 uint32_t pid,
+                                                 int64_t timestamp) {
+  DescriptorTrackReservation reservation;
+  reservation.min_timestamp = timestamp;
+  reservation.pid = pid;
 
-#if PERFETTO_DLOG_IS_ON()
-    if (upid) {
-      // Verify that upid didn't change.
-      auto process_track_row =
-          context_->storage->process_track_table().id().IndexOf(track_id);
-      if (!process_track_row) {
-        PERFETTO_DLOG("Can't update non-scoped track with uuid %" PRIu64
-                      " to a scoped track.",
-                      uuid);
-      } else {
-        auto old_upid =
-            context_->storage->process_track_table().upid()[*process_track_row];
-        if (old_upid != upid) {
-          PERFETTO_DLOG("Ignoring upid change for track with uuid %" PRIu64
-                        " from %" PRIu32 " to %" PRIu32 ".",
-                        uuid, old_upid, *upid);
-        }
-      }
-    }
+  std::map<uint64_t, DescriptorTrackReservation>::iterator it;
+  bool inserted;
+  std::tie(it, inserted) =
+      reserved_descriptor_tracks_.insert(std::make_pair<>(uuid, reservation));
 
-    if (utid) {
-      // Verify that utid didn't change.
-      auto thread_track_row =
-          context_->storage->thread_track_table().id().IndexOf(track_id);
-      if (!thread_track_row) {
-        PERFETTO_DLOG("Can't update non-thread track with uuid %" PRIu64
-                      " to a thread track.",
-                      uuid);
-      } else {
-        auto old_utid =
-            context_->storage->thread_track_table().utid()[*thread_track_row];
-        if (old_utid != utid) {
-          PERFETTO_DLOG("Ignoring utid change for track with uuid %" PRIu64
-                        " from %" PRIu32 " to %" PRIu32 ".",
-                        uuid, old_utid, *utid);
-        }
-      }
-    }
-#endif  // PERFETTO_DLOG_IS_ON()
+  if (inserted)
+    return;
 
-    return track_id;
+  if (!it->second.IsForSameTrack(reservation)) {
+    // Process tracks should not be reassigned to a different pid later (neither
+    // should the type of the track change).
+    PERFETTO_DLOG("New track reservation for process track with uuid %" PRIu64
+                  " doesn't match earlier one",
+                  uuid);
+    context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+    return;
   }
 
-  TrackId track_id;
-
-  if (utid) {
-    // Update existing track for the thread if we have previously created one
-    // in GetOrCreateDescriptorTrackForThread().
-    auto utid_it = descriptor_tracks_by_utid_.find(*utid);
-    if (utid_it != descriptor_tracks_by_utid_.end()) {
-      TrackId candidate_track_id = utid_it->second;
-      // Only update this track if it hasn't been associated with a different
-      // UUID already.
-      auto descriptor_it = std::find_if(
-          descriptor_tracks_.begin(), descriptor_tracks_.end(),
-          [candidate_track_id](const std::pair<uint64_t, TrackId>& entry) {
-            return entry.second == candidate_track_id;
-          });
-      if (descriptor_it == descriptor_tracks_.end()) {
-        descriptor_tracks_[uuid] = candidate_track_id;
-        context_->args_tracker->AddArg(
-            TableId::kTrack, candidate_track_id.value, source_id_key_,
-            source_id_key_, Variadic::Integer(static_cast<int64_t>(uuid)));
-
-        return candidate_track_id;
-      }
-    }
-
-    // New thread track.
-    tables::ThreadTrackTable::Row row(name);
-    row.utid = *utid;
-    track_id = context_->storage->mutable_thread_track_table()->Insert(row);
-    if (descriptor_tracks_by_utid_.find(*utid) ==
-        descriptor_tracks_by_utid_.end()) {
-      descriptor_tracks_by_utid_[*utid] = track_id;
-    }
-  } else if (upid) {
-    // New process-scoped async track.
-    tables::ProcessTrackTable::Row track(name);
-    track.upid = *upid;
-    track_id = context_->storage->mutable_process_track_table()->Insert(track);
-  } else {
-    // New global async track.
-    tables::TrackTable::Row track(name);
-    track_id = context_->storage->mutable_track_table()->Insert(track);
-  }
-
-  descriptor_tracks_[uuid] = track_id;
-
-  ArgsTracker::BoundInserter inserter(context_->args_tracker.get(),
-                                      TableId::kTrack, track_id.value);
-  inserter.AddArg(source_key_, Variadic::String(descriptor_source_));
-  inserter.AddArg(source_id_key_,
-                  Variadic::Integer(static_cast<int64_t>(uuid)));
-  return track_id;
+  it->second.min_timestamp = std::min(it->second.min_timestamp, timestamp);
 }
 
-base::Optional<TrackId> TrackTracker::GetDescriptorTrack(uint64_t uuid) const {
-  auto it = descriptor_tracks_.find(uuid);
-  if (it == descriptor_tracks_.end())
-    return base::nullopt;
+void TrackTracker::ReserveDescriptorThreadTrack(uint64_t uuid,
+                                                uint64_t parent_uuid,
+                                                uint32_t pid,
+                                                uint32_t tid,
+                                                int64_t timestamp) {
+  DescriptorTrackReservation reservation;
+  reservation.min_timestamp = timestamp;
+  reservation.parent_uuid = parent_uuid;
+  reservation.pid = pid;
+  reservation.tid = tid;
+
+  std::map<uint64_t, DescriptorTrackReservation>::iterator it;
+  bool inserted;
+  std::tie(it, inserted) =
+      reserved_descriptor_tracks_.insert(std::make_pair<>(uuid, reservation));
+
+  if (inserted)
+    return;
+
+  if (!it->second.IsForSameTrack(reservation)) {
+    // Thread tracks should not be reassigned to a different pid/tid later
+    // (neither should the type of the track change).
+    PERFETTO_DLOG("New track reservation for thread track with uuid %" PRIu64
+                  " doesn't match earlier one",
+                  uuid);
+    context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+    return;
+  }
+
+  it->second.min_timestamp = std::min(it->second.min_timestamp, timestamp);
+}
+
+void TrackTracker::ReserveDescriptorChildTrack(uint64_t uuid,
+                                               uint64_t parent_uuid) {
+  DescriptorTrackReservation reservation;
+  reservation.parent_uuid = parent_uuid;
+
+  std::map<uint64_t, DescriptorTrackReservation>::iterator it;
+  bool inserted;
+  std::tie(it, inserted) =
+      reserved_descriptor_tracks_.insert(std::make_pair<>(uuid, reservation));
+
+  if (inserted || it->second.IsForSameTrack(reservation))
+    return;
+
+  // Child tracks should not be reassigned to a different parent track later
+  // (neither should the type of the track change).
+  PERFETTO_DLOG("New track reservation for child track with uuid %" PRIu64
+                " doesn't match earlier one",
+                uuid);
+  context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+}
+
+base::Optional<TrackId> TrackTracker::GetDescriptorTrack(uint64_t uuid) {
+  auto it = resolved_descriptor_tracks_.find(uuid);
+  if (it == resolved_descriptor_tracks_.end()) {
+    auto reservation_it = reserved_descriptor_tracks_.find(uuid);
+    if (reservation_it == reserved_descriptor_tracks_.end())
+      return base::nullopt;
+    TrackId track_id = ResolveDescriptorTrack(uuid, reservation_it->second);
+    resolved_descriptor_tracks_[uuid] = track_id;
+    return track_id;
+  }
   return it->second;
 }
 
-TrackId TrackTracker::GetOrCreateDescriptorTrackForThread(UniqueTid utid) {
-  auto it = descriptor_tracks_by_utid_.find(utid);
-  if (it != descriptor_tracks_by_utid_.end()) {
-    return it->second;
+TrackId TrackTracker::ResolveDescriptorTrack(
+    uint64_t uuid,
+    const DescriptorTrackReservation& reservation) {
+  base::Optional<TrackId> parent_track_id;
+  if (reservation.parent_uuid) {
+    // Ensure that parent track is resolved.
+    parent_track_id = GetDescriptorTrack(reservation.parent_uuid);
+    if (!parent_track_id) {
+      PERFETTO_ELOG("Unknown parent track %" PRIu64 " for track %" PRIu64,
+                    reservation.parent_uuid, uuid);
+    }
   }
-  // TODO(eseckler): How should this track receive its name?
-  tables::ThreadTrackTable::Row row(/*name=*/kNullStringId);
-  row.utid = utid;
-  TrackId track_id =
-      context_->storage->mutable_thread_track_table()->Insert(row);
-  descriptor_tracks_by_utid_[utid] = track_id;
 
-  context_->args_tracker->AddArg(TableId::kTrack, track_id.value, source_key_,
-                                 source_key_,
-                                 Variadic::String(descriptor_source_));
-  return track_id;
+  if (reservation.tid) {
+    UniqueTid utid = context_->process_tracker->UpdateThread(*reservation.tid,
+                                                             *reservation.pid);
+    auto it_and_inserted =
+        descriptor_uuids_by_utid_.insert(std::make_pair<>(utid, uuid));
+    if (!it_and_inserted.second) {
+      // We already saw a another track with a different uuid for this thread.
+      // Since there should only be one descriptor track for each thread, we
+      // assume that its tid was reused. So, start a new thread.
+      uint64_t old_uuid = it_and_inserted.first->second;
+      PERFETTO_DCHECK(old_uuid != uuid);  // Every track is only resolved once.
+
+      PERFETTO_DLOG("Detected tid reuse (pid: %" PRIu32 " tid: %" PRIu32
+                    ") from track descriptors (old uuid: %" PRIu64
+                    " new uuid: %" PRIu64 " timestamp: %" PRId64 ")",
+                    *reservation.pid, *reservation.tid, old_uuid, uuid,
+                    reservation.min_timestamp);
+
+      utid = context_->process_tracker->StartNewThread(
+          base::nullopt, *reservation.tid, kNullStringId);
+
+      // Associate the new thread with its process.
+      PERFETTO_CHECK(context_->process_tracker->UpdateThread(
+                         *reservation.tid, *reservation.pid) == utid);
+
+      descriptor_uuids_by_utid_[utid] = uuid;
+    }
+    return InternThreadTrack(utid);
+  }
+
+  if (reservation.pid) {
+    UniquePid upid =
+        context_->process_tracker->GetOrCreateProcess(*reservation.pid);
+    auto it_and_inserted =
+        descriptor_uuids_by_upid_.insert(std::make_pair<>(upid, uuid));
+    if (!it_and_inserted.second) {
+      // We already saw a another track with a different uuid for this process.
+      // Since there should only be one descriptor track for each process, we
+      // assume that its pid was reused. So, start a new process.
+      uint64_t old_uuid = it_and_inserted.first->second;
+      PERFETTO_DCHECK(old_uuid != uuid);  // Every track is only resolved once.
+
+      PERFETTO_DLOG("Detected pid reuse (pid: %" PRIu32
+                    ") from track descriptors (old uuid: %" PRIu64
+                    " new uuid: %" PRIu64 " timestamp: %" PRId64 ")",
+                    *reservation.pid, old_uuid, uuid,
+                    reservation.min_timestamp);
+
+      upid = context_->process_tracker->StartNewProcess(
+          base::nullopt, base::nullopt, *reservation.pid, kNullStringId);
+
+      descriptor_uuids_by_upid_[upid] = uuid;
+    }
+    return InternProcessTrack(upid);
+  }
+
+  base::Optional<TrackId> track_id;
+  if (parent_track_id) {
+    // If parent is a thread track, create another thread-associated track.
+    base::Optional<uint32_t> thread_track_index =
+        context_->storage->thread_track_table().id().IndexOf(*parent_track_id);
+    if (thread_track_index) {
+      auto* thread_tracks = context_->storage->mutable_thread_track_table();
+      tables::ThreadTrackTable::Row row;
+      row.utid = thread_tracks->utid()[*thread_track_index];
+      track_id = thread_tracks->Insert(row);
+    } else {
+      // If parent is a process track, create another process-associated track.
+      base::Optional<uint32_t> process_track_index =
+          context_->storage->process_track_table().id().IndexOf(
+              *parent_track_id);
+      if (process_track_index) {
+        auto* process_tracks = context_->storage->mutable_process_track_table();
+        tables::ProcessTrackTable::Row track;
+        track.upid = process_tracks->upid()[*process_track_index];
+        track_id = process_tracks->Insert(track);
+      }
+    }
+  }
+
+  // Otherwise create a global track.
+  if (!track_id) {
+    tables::TrackTable::Row track;
+    track_id = context_->storage->mutable_track_table()->Insert(track);
+    // The global track with no uuid is the default global track (e.g. for
+    // global instant events). Any other global tracks are considered children
+    // of the default track.
+    if (!parent_track_id && uuid)
+      parent_track_id = GetOrCreateDefaultDescriptorTrack();
+  }
+
+  auto args = context_->args_tracker->AddArgsTo(*track_id);
+  args.AddArg(source_key_, Variadic::String(descriptor_source_))
+      .AddArg(source_id_key_, Variadic::Integer(static_cast<int64_t>(uuid)));
+  if (parent_track_id) {
+    args.AddArg(parent_track_id_key_,
+                Variadic::Integer(parent_track_id->value));
+  }
+  return *track_id;
 }
 
 TrackId TrackTracker::GetOrCreateDefaultDescriptorTrack() {
-  base::Optional<TrackId> opt_track_id =
+  // If the default track was already reserved (e.g. because a producer emitted
+  // a descriptor for it) or created, resolve and return it.
+  base::Optional<TrackId> track_id =
       GetDescriptorTrack(kDefaultDescriptorTrackUuid);
-  if (opt_track_id)
-    return *opt_track_id;
+  if (track_id)
+    return *track_id;
 
-  return UpdateDescriptorTrack(kDefaultDescriptorTrackUuid,
-                               default_descriptor_track_name_);
+  // Otherwise reserve a new track and resolve it.
+  ReserveDescriptorChildTrack(kDefaultDescriptorTrackUuid, /*parent_uuid=*/0);
+  track_id = GetDescriptorTrack(kDefaultDescriptorTrackUuid);
+
+  auto* tracks = context_->storage->mutable_track_table();
+  tracks->mutable_name()->Set(*tracks->id().IndexOf(*track_id),
+                              default_descriptor_track_name_);
+  return *track_id;
 }
 
 TrackId TrackTracker::InternGlobalCounterTrack(StringId name) {
diff --git a/src/trace_processor/track_tracker.h b/src/trace_processor/track_tracker.h
index 3a2b0ba..ddd6f11 100644
--- a/src/trace_processor/track_tracker.h
+++ b/src/trace_processor/track_tracker.h
@@ -31,6 +31,9 @@
   // Interns a thread track into the storage.
   TrackId InternThreadTrack(UniqueTid utid);
 
+  // Interns a process track into the storage.
+  TrackId InternProcessTrack(UniquePid upid);
+
   // Interns a Fuchsia async track into the storage.
   TrackId InternFuchsiaAsyncTrack(StringId name, int64_t correlation_id);
 
@@ -56,21 +59,50 @@
   // Lazily creates the track for legacy Chrome global instant events.
   TrackId GetOrCreateLegacyChromeGlobalInstantTrack();
 
-  // Create or update the track for the TrackDescriptor with the given |uuid|.
-  // Optionally, associate the track with a process or thread.
-  TrackId UpdateDescriptorTrack(uint64_t uuid,
-                                StringId name,
-                                base::Optional<UniquePid> upid = base::nullopt,
-                                base::Optional<UniqueTid> utid = base::nullopt);
+  // Associate a TrackDescriptor track identified by the given |uuid| with a
+  // process's |pid|. This is called during tokenization. If a reservation for
+  // the same |uuid| already exists, verifies that the present reservation
+  // matches the new one.
+  //
+  // The track will be resolved to the process track (see InternProcessTrack())
+  // upon the first call to GetDescriptorTrack() with the same |uuid|. At this
+  // time, |pid| will also be resolved to a |upid|.
+  void ReserveDescriptorProcessTrack(uint64_t uuid,
+                                     uint32_t pid,
+                                     int64_t timestamp);
+
+  // Associate a TrackDescriptor track identified by the given |uuid| with a
+  // thread's |pid| and |tid|. This is called during tokenization. If a
+  // reservation for the same |uuid| already exists, verifies that the present
+  // reservation matches the new one.
+  //
+  // The track will be resolved to the thread track (see InternThreadTrack())
+  // upon the first call to GetDescriptorTrack() with the same |uuid|. At this
+  // time, |pid| will also be resolved to a |upid|.
+  void ReserveDescriptorThreadTrack(uint64_t uuid,
+                                    uint64_t parent_uuid,
+                                    uint32_t pid,
+                                    uint32_t tid,
+                                    int64_t timestamp);
+
+  // Associate a TrackDescriptor track identified by the given |uuid| with a
+  // parent track (usually a process- or thread-associated track). This is
+  // called during tokenization. If a reservation for the same |uuid| already
+  // exists, will attempt to update it.
+  //
+  // The track will be created upon the first call to GetDescriptorTrack() with
+  // the same |uuid|. If |parent_uuid| is 0, the track will become a global
+  // track. Otherwise, it will become a new track of the same type as its parent
+  // track.
+  void ReserveDescriptorChildTrack(uint64_t uuid, uint64_t parent_uuid);
 
   // Returns the ID of the track for the TrackDescriptor with the given |uuid|.
-  // Returns nullopt if no TrackDescriptor with this |uuid| has been parsed yet.
-  base::Optional<TrackId> GetDescriptorTrack(uint64_t uuid) const;
-
-  // Returns the ID of the TrackDescriptor track associated with the given utid.
-  // If the trace contained multiple tracks associated with the utid, the first
-  // created track is returned. Creates a new track if no such track exists.
-  TrackId GetOrCreateDescriptorTrackForThread(UniqueTid utid);
+  // This is called during parsing. The first call to GetDescriptorTrack() for
+  // each |uuid| resolves and inserts the track (and its parent tracks,
+  // following the parent_uuid chain recursively) based on reservations made for
+  // the |uuid|. Returns nullopt if no track for a descriptor with this |uuid|
+  // has been reserved.
+  base::Optional<TrackId> GetDescriptorTrack(uint64_t uuid);
 
   // Returns the ID of the implicit trace-global default TrackDescriptor track.
   TrackId GetOrCreateDefaultDescriptorTrack();
@@ -99,8 +131,8 @@
   // Creates a counter track associated with a GPU into the storage.
   TrackId CreateGpuCounterTrack(StringId name,
                                 uint32_t gpu_id,
-                                StringId description = 0,
-                                StringId unit = 0);
+                                StringId description = StringId::Null(),
+                                StringId unit = StringId::Null());
 
  private:
   struct GpuTrackTuple {
@@ -116,7 +148,7 @@
   struct ChromeTrackTuple {
     base::Optional<int64_t> upid;
     int64_t source_id = 0;
-    StringId source_scope = 0;
+    StringId source_scope = StringId::Null();
 
     friend bool operator<(const ChromeTrackTuple& l,
                           const ChromeTrackTuple& r) {
@@ -135,18 +167,37 @@
              std::tie(r.upid, r.cookie, r.name);
     }
   };
+  struct DescriptorTrackReservation {
+    uint64_t parent_uuid = 0;
+    base::Optional<uint32_t> pid;
+    base::Optional<uint32_t> tid;
+    int64_t min_timestamp = 0;  // only set if |pid| and/or |tid| is set.
+
+    // Whether |other| is a valid descriptor for this track reservation. A track
+    // should always remain nested underneath its original parent.
+    bool IsForSameTrack(const DescriptorTrackReservation& other) {
+      // Note that |timestamp| is ignored for this comparison.
+      return std::tie(parent_uuid, pid, tid) ==
+             std::tie(other.parent_uuid, pid, tid);
+    }
+  };
+
+  TrackId ResolveDescriptorTrack(uint64_t uuid,
+                                 const DescriptorTrackReservation&);
 
   static constexpr uint64_t kDefaultDescriptorTrackUuid = 0u;
 
-  std::map<UniqueTid /* utid */, TrackId> thread_tracks_;
+  std::map<UniqueTid, TrackId> thread_tracks_;
+  std::map<UniquePid, TrackId> process_tracks_;
   std::map<int64_t /* correlation_id */, TrackId> fuchsia_async_tracks_;
   std::map<GpuTrackTuple, TrackId> gpu_tracks_;
   std::map<ChromeTrackTuple, TrackId> chrome_tracks_;
   std::map<AndroidAsyncTrackTuple, TrackId> android_async_tracks_;
   std::map<UniquePid, TrackId> chrome_process_instant_tracks_;
   base::Optional<TrackId> chrome_global_instant_track_id_;
-  std::map<uint64_t /* uuid */, TrackId> descriptor_tracks_;
-  std::map<UniqueTid, TrackId> descriptor_tracks_by_utid_;
+  std::map<uint64_t /* uuid */, DescriptorTrackReservation>
+      reserved_descriptor_tracks_;
+  std::map<uint64_t /* uuid */, TrackId> resolved_descriptor_tracks_;
 
   std::map<StringId, TrackId> global_counter_tracks_by_name_;
   std::map<std::pair<StringId, uint32_t>, TrackId> cpu_counter_tracks_;
@@ -156,17 +207,23 @@
   std::map<std::pair<StringId, int32_t>, TrackId> softirq_counter_tracks_;
   std::map<std::pair<StringId, uint32_t>, TrackId> gpu_counter_tracks_;
 
-  const StringId source_key_ = 0;
-  const StringId source_id_key_ = 0;
-  const StringId source_id_is_process_scoped_key_ = 0;
-  const StringId source_scope_key_ = 0;
+  // Stores the descriptor uuid used for the primary process/thread track
+  // for the given upid / utid. Used for pid/tid reuse detection.
+  std::map<UniquePid, uint64_t /*uuid*/> descriptor_uuids_by_upid_;
+  std::map<UniqueTid, uint64_t /*uuid*/> descriptor_uuids_by_utid_;
 
-  const StringId fuchsia_source_ = 0;
-  const StringId chrome_source_ = 0;
-  const StringId android_source_ = 0;
-  const StringId descriptor_source_ = 0;
+  const StringId source_key_ = kNullStringId;
+  const StringId source_id_key_ = kNullStringId;
+  const StringId source_id_is_process_scoped_key_ = kNullStringId;
+  const StringId source_scope_key_ = kNullStringId;
+  const StringId parent_track_id_key_ = kNullStringId;
 
-  const StringId default_descriptor_track_name_ = 0;
+  const StringId fuchsia_source_ = kNullStringId;
+  const StringId chrome_source_ = kNullStringId;
+  const StringId android_source_ = kNullStringId;
+  const StringId descriptor_source_ = kNullStringId;
+
+  const StringId default_descriptor_track_name_ = kNullStringId;
 
   TraceProcessorContext* const context_;
 };
diff --git a/src/trace_processor/types/BUILD.gn b/src/trace_processor/types/BUILD.gn
new file mode 100644
index 0000000..b86ba23
--- /dev/null
+++ b/src/trace_processor/types/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright (C) 2020 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.
+
+source_set("types") {
+  sources = [
+    "gfp_flags.cc",
+    "gfp_flags.h",
+    "variadic.cc",
+    "variadic.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/base",
+    "../containers",
+  ]
+}
diff --git a/src/trace_processor/gfp_flags.cc b/src/trace_processor/types/gfp_flags.cc
similarity index 99%
rename from src/trace_processor/gfp_flags.cc
rename to src/trace_processor/types/gfp_flags.cc
index 2d5b587..74f7511 100644
--- a/src/trace_processor/gfp_flags.cc
+++ b/src/trace_processor/types/gfp_flags.cc
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/gfp_flags.h"
+#include "src/trace_processor/types/gfp_flags.h"
+
 #include <array>
 
 namespace perfetto {
diff --git a/src/trace_processor/gfp_flags.h b/src/trace_processor/types/gfp_flags.h
similarity index 88%
rename from src/trace_processor/gfp_flags.h
rename to src/trace_processor/types/gfp_flags.h
index 9d4cd7d..837b284 100644
--- a/src/trace_processor/gfp_flags.h
+++ b/src/trace_processor/types/gfp_flags.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_GFP_FLAGS_H_
-#define SRC_TRACE_PROCESSOR_GFP_FLAGS_H_
+#ifndef SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
+#define SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
 
 #include <tuple>
 #include "perfetto/ext/base/string_writer.h"
@@ -33,4 +33,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_GFP_FLAGS_H_
+#endif  // SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
diff --git a/test/task_runner_thread_delegates.cc b/src/trace_processor/types/variadic.cc
similarity index 68%
copy from test/task_runner_thread_delegates.cc
copy to src/trace_processor/types/variadic.cc
index 291482f..837bfeb 100644
--- a/test/task_runner_thread_delegates.cc
+++ b/src/trace_processor/types/variadic.cc
@@ -1,5 +1,5 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
+#/*
+ * Copyright (C) 2018 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.
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "test/task_runner_thread_delegates.h"
+#include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
+namespace trace_processor {
 
-ServiceDelegate::~ServiceDelegate() = default;
-ProbesProducerDelegate::~ProbesProducerDelegate() = default;
-FakeProducerDelegate::~FakeProducerDelegate() = default;
+constexpr const char* Variadic::kTypeNames[];
 
+}  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/variadic.h b/src/trace_processor/types/variadic.h
similarity index 89%
rename from src/trace_processor/variadic.h
rename to src/trace_processor/types/variadic.h
index 0444721..7a929e5 100644
--- a/src/trace_processor/variadic.h
+++ b/src/trace_processor/types/variadic.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_VARIADIC_H_
-#define SRC_TRACE_PROCESSOR_VARIADIC_H_
+#ifndef SRC_TRACE_PROCESSOR_TYPES_VARIADIC_H_
+#define SRC_TRACE_PROCESSOR_TYPES_VARIADIC_H_
 
 #include "src/trace_processor/containers/string_pool.h"
 
@@ -24,7 +24,19 @@
 
 // Variadic type representing value of different possible types.
 struct Variadic {
-  enum Type { kInt, kUint, kString, kReal, kPointer, kBool, kJson };
+  enum Type : size_t {
+    kInt,
+    kUint,
+    kString,
+    kReal,
+    kPointer,
+    kBool,
+    kJson,
+    kMaxType = kJson,
+  };
+
+  static constexpr const char* const kTypeNames[] = {
+      "int", "uint", "string", "real", "pointer", "bool", "json"};
 
   static Variadic Integer(int64_t int_value) {
     Variadic variadic;
@@ -121,4 +133,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_VARIADIC_H_
+#endif  // SRC_TRACE_PROCESSOR_TYPES_VARIADIC_H_
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 6acb64a..4bdfba5 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -22,44 +22,37 @@
     "../../../gn:default_deps",
     "../../../include/perfetto/ext/traced",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
   assert_no_deps = [ "//gn:protobuf_lite" ]
 }
 
 # Contains all the implementation but not the main() entry point. This target
 # is shared both by the executable and tests.
 source_set("probes") {
-  public_deps = [
-    "../../../include/perfetto/ext/traced",
-  ]
+  public_deps = [ "../../../include/perfetto/ext/traced" ]
   deps = [
     ":probes_src",
     "../../../gn:default_deps",
-    "../../tracing:ipc",
+    "../../tracing/ipc/producer",
   ]
   if (enable_perfetto_version_gen) {
     deps += [ "//gn/standalone:gen_git_revision" ]
   }
-  sources = [
-    "probes.cc",
-  ]
+  sources = [ "probes.cc" ]
 }
 
 source_set("probes_src") {
-  public_deps = [
-    "ftrace",
-  ]
+  public_deps = [ "ftrace" ]
   deps = [
     ":data_source",
     "../../../gn:default_deps",
     "../../../include/perfetto/ext/traced",
     "../../../protos/perfetto/config/ftrace:cpp",
+    "../../../protos/perfetto/trace:zero",
     "../../../protos/perfetto/trace/ps:zero",
     "../../base",
-    "../../tracing:ipc",
-    "../../tracing:tracing",
+    "../../tracing/core",
+    "../../tracing/ipc/producer",
     "android_log",
     "filesystem",
     "metatrace",
@@ -79,7 +72,7 @@
 source_set("data_source") {
   deps = [
     "../../../gn:default_deps",
-    "../../tracing",
+    "../../tracing/core",
   ]
   sources = [
     "probes_data_source.cc",
@@ -93,7 +86,7 @@
     ":probes_src",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
-    "../../tracing:test_support",
+    "../../tracing/test:test_support",
     "android_log:unittests",
     "filesystem:unittests",
     "packages_list:unittests",
diff --git a/src/traced/probes/android_log/BUILD.gn b/src/traced/probes/android_log/BUILD.gn
index cc25cd4..9c3dc72 100644
--- a/src/traced/probes/android_log/BUILD.gn
+++ b/src/traced/probes/android_log/BUILD.gn
@@ -15,15 +15,14 @@
 import("../../../../gn/test.gni")
 
 source_set("android_log") {
-  public_deps = [
-    "../../../tracing",
-  ]
+  public_deps = [ "../../../tracing/core" ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
     "../../../../protos/perfetto/common:zero",
     "../../../../protos/perfetto/config/android:zero",
+    "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/android:zero",
     "../../../base",
   ]
@@ -43,9 +42,7 @@
     "../../../../protos/perfetto/config/android:cpp",
     "../../../../protos/perfetto/trace/android:cpp",
     "../../../../src/base:test_support",
-    "../../../../src/tracing:test_support",
+    "../../../../src/tracing/test:test_support",
   ]
-  sources = [
-    "android_log_data_source_unittest.cc",
-  ]
+  sources = [ "android_log_data_source_unittest.cc" ]
 }
diff --git a/src/traced/probes/filesystem/BUILD.gn b/src/traced/probes/filesystem/BUILD.gn
index d3aeba5..faf101a 100644
--- a/src/traced/probes/filesystem/BUILD.gn
+++ b/src/traced/probes/filesystem/BUILD.gn
@@ -17,13 +17,14 @@
 source_set("filesystem") {
   public_deps = [
     "../../../../protos/perfetto/trace/filesystem:zero",
-    "../../../tracing",
+    "../../../tracing/core",
   ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
     "../../../../protos/perfetto/config/inode_file:zero",
+    "../../../../protos/perfetto/trace:zero",
     "../../../base",
   ]
   sources = [
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
index ac5275c..8ccab1a 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.h
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -25,8 +25,8 @@
 #include <string>
 #include <unordered_map>
 
+#include "perfetto/base/flat_set.h"
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/flat_set.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/traced/data_source_types.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index 02ef384..5d9cd70 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -32,9 +32,7 @@
     "../../../base:test_support",
     "../../../protozero",
   ]
-  public_deps = [
-    "../../../protozero",
-  ]
+  public_deps = [ "../../../protozero" ]
 
   sources = [
     "test/cpu_reader_support.cc",
@@ -57,7 +55,7 @@
     "../../../../protos/perfetto/trace/ftrace:cpp",
     "../../../../protos/perfetto/trace/ftrace:zero",
     "../../../base:test_support",
-    "../../../tracing:test_support",
+    "../../../tracing/test:test_support",
   ]
   sources = [
     "cpu_reader_unittest.cc",
@@ -78,9 +76,7 @@
     "lite",
     "zero",
   ]
-  sources = [
-    "test/test_messages.proto",
-  ]
+  sources = [ "test/test_messages.proto" ]
   proto_path = perfetto_root_path
 }
 
@@ -94,24 +90,23 @@
     "../../../../gn:default_deps",
     "../../../../gn:gtest_and_gmock",
     "../../../base",
-    "../../../tracing",
+    "../../../tracing/core",
   ]
-  sources = [
-    "ftrace_procfs_integrationtest.cc",
-  ]
+  sources = [ "ftrace_procfs_integrationtest.cc" ]
 }
 
 source_set("ftrace") {
   public_deps = [
     "../../../../protos/perfetto/config/ftrace:cpp",
     "../../../../protos/perfetto/trace/ftrace:zero",
-    "../../../tracing",
+    "../../../tracing/core",
   ]
   deps = [
     ":format_parser",
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
+    "../../../../protos/perfetto/trace:zero",
     "../../../android_internal:lazy_library_loader",
     "../../../base",
     "../../../protozero",
@@ -169,17 +164,13 @@
       "../../../../gn:benchmark",
       "../../../../gn:default_deps",
     ]
-    sources = [
-      "cpu_reader_benchmark.cc",
-    ]
+    sources = [ "cpu_reader_benchmark.cc" ]
   }
 }
 
 perfetto_fuzzer_test("cpu_reader_fuzzer") {
   testonly = true
-  sources = [
-    "cpu_reader_fuzzer.cc",
-  ]
+  sources = [ "cpu_reader_fuzzer.cc" ]
   deps = [
     ":ftrace",
     ":test_support",
diff --git a/src/traced/probes/ftrace/cpu_reader_benchmark.cc b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
index 58b4e20..ee6d246 100644
--- a/src/traced/probes/ftrace/cpu_reader_benchmark.cc
+++ b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
@@ -315,8 +315,8 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config{
+      EventFilter{}, DisabledCompactSchedConfigForTesting(), {}, {}};
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
diff --git a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
index 61d5b80..27e5db3 100644
--- a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
+++ b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
@@ -52,8 +52,8 @@
   memcpy(g_page, data, std::min(base::kPageSize, size));
 
   FtraceMetadata metadata{};
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config{
+      EventFilter{}, DisabledCompactSchedConfigForTesting(), {}, {}};
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
   ds_config.event_filter.AddEnabledEvent(
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 61cdc23..2d10829 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -60,6 +60,11 @@
 
 namespace {
 
+FtraceDataSourceConfig EmptyConfig() {
+  return FtraceDataSourceConfig{
+      EventFilter{}, DisabledCompactSchedConfigForTesting(), {}, {}};
+}
+
 constexpr uint64_t kNanoInSecond = 1000 * 1000 * 1000;
 constexpr uint64_t kNanoInMicro = 1000;
 
@@ -378,8 +383,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
@@ -507,8 +511,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
@@ -557,8 +560,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
@@ -598,8 +600,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
 
   FtraceMetadata metadata{};
   CompactSchedBuffer compact_buffer;
@@ -662,8 +663,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
@@ -772,8 +772,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
@@ -819,8 +818,8 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   EnabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config{
+      EventFilter{}, EnabledCompactSchedConfigForTesting(), {}, {}};
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
@@ -1160,8 +1159,7 @@
   BundleProvider bundle_provider(base::kPageSize);
   ProtoTranslationTable* table = GetTable("synthetic");
   FtraceMetadata metadata{};
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
@@ -1618,8 +1616,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
@@ -2063,8 +2060,7 @@
   ProtoTranslationTable* table = GetTable(test_case->name);
   auto page = PageFromXxd(test_case->data);
 
-  FtraceDataSourceConfig ds_config{EventFilter{},
-                                   DisabledCompactSchedConfigForTesting()};
+  FtraceDataSourceConfig ds_config = EmptyConfig();
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
diff --git a/src/traced/probes/ftrace/format_parser.cc b/src/traced/probes/ftrace/format_parser.cc
index c5ce2c1..d4e119f 100644
--- a/src/traced/probes/ftrace/format_parser.cc
+++ b/src/traced/probes/ftrace/format_parser.cc
@@ -141,7 +141,7 @@
 
   for (base::StringSplitter ss(std::move(input), '\n'); ss.Next();) {
     const char* line = ss.cur_token();
-    if (!has_id && sscanf(line, "ID: %d", &id) == 1) {
+    if (!has_id && sscanf(line, "ID: %u", &id) == 1) {
       has_id = true;
       continue;
     }
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index f0cf926..5a93e5f 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <iterator>
 
 #include "perfetto/ext/base/utils.h"
 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
@@ -67,6 +68,28 @@
                         event.substr(slash_pos + 1));
 }
 
+void UnionInPlace(const std::vector<std::string>& unsorted_a,
+                  std::vector<std::string>* out) {
+  std::vector<std::string> a = unsorted_a;
+  std::sort(a.begin(), a.end());
+  std::sort(out->begin(), out->end());
+  std::vector<std::string> v;
+  std::set_union(a.begin(), a.end(), out->begin(), out->end(),
+                 std::back_inserter(v));
+  *out = std::move(v);
+}
+
+void IntersectInPlace(const std::vector<std::string>& unsorted_a,
+                      std::vector<std::string>* out) {
+  std::vector<std::string> a = unsorted_a;
+  std::sort(a.begin(), a.end());
+  std::sort(out->begin(), out->end());
+  std::vector<std::string> v;
+  std::set_intersection(a.begin(), a.end(), out->begin(), out->end(),
+                        std::back_inserter(v));
+  *out = std::move(v);
+}
+
 }  // namespace
 
 std::set<GroupAndName> FtraceConfigMuxer::GetFtraceEvents(
@@ -453,10 +476,14 @@
   auto compact_sched =
       CreateCompactSchedConfig(request, table_->compact_sched_format());
 
-  FtraceConfigId id = ++last_id_;
-  ds_configs_.emplace(std::piecewise_construct, std::forward_as_tuple(id),
-                      std::forward_as_tuple(std::move(filter), compact_sched));
+  std::vector<std::string> apps(request.atrace_apps());
+  std::vector<std::string> categories(request.atrace_categories());
 
+  FtraceConfigId id = ++last_id_;
+  ds_configs_.emplace(
+      std::piecewise_construct, std::forward_as_tuple(id),
+      std::forward_as_tuple(std::move(filter), compact_sched, std::move(apps),
+                            std::move(categories)));
   return id;
 }
 
@@ -487,9 +514,29 @@
   if (!config_id || !ds_configs_.erase(config_id))
     return false;
   EventFilter expected_ftrace_events;
-  for (const auto& ds_config : ds_configs_) {
-    expected_ftrace_events.EnableEventsFrom(ds_config.second.event_filter);
+  std::vector<std::string> expected_apps;
+  std::vector<std::string> expected_categories;
+  for (const auto& id_config : ds_configs_) {
+    const perfetto::FtraceDataSourceConfig& config = id_config.second;
+    expected_ftrace_events.EnableEventsFrom(config.event_filter);
+    UnionInPlace(config.atrace_apps, &expected_apps);
+    UnionInPlace(config.atrace_categories, &expected_categories);
   }
+  // At this point expected_{apps,categories} contains the union of the
+  // leftover configs (if any) that should be still on. However we did not
+  // necessarily succeed in turning on atrace for each of those configs
+  // previously so we now intersect the {apps,categories} that we *did* manage
+  // to turn on with those we want on to determine the new state we should aim
+  // for:
+  IntersectInPlace(current_state_.atrace_apps, &expected_apps);
+  IntersectInPlace(current_state_.atrace_categories, &expected_categories);
+  // Work out if there is any difference between the current state and the
+  // desired state: It's sufficient to compare sizes here (since we know from
+  // above that expected_{apps,categories} is now a subset of
+  // atrace_{apps,categories}:
+  bool atrace_changed =
+      (current_state_.atrace_apps.size() != expected_apps.size()) ||
+      (current_state_.atrace_categories.size() != expected_categories.size());
 
   // Disable any events that are currently enabled, but are not in any configs
   // anymore.
@@ -524,8 +571,22 @@
       current_state_.cpu_buffer_size_pages = 1;
     ftrace_->DisableAllEvents();
     ftrace_->ClearTrace();
-    if (current_state_.atrace_on)
+  }
+
+  if (current_state_.atrace_on) {
+    if (expected_apps.empty() && expected_categories.empty()) {
       DisableAtrace();
+    } else if (atrace_changed) {
+      // Update atrace to remove the no longer wanted categories/apps. For
+      // some categories this won't disable them (e.g. categories that just
+      // enable ftrace events) for those there is nothing we can do till the
+      // last ftrace config is removed.
+      if (StartAtrace(expected_apps, expected_categories)) {
+        // Update current_state_ to reflect this change.
+        current_state_.atrace_apps = expected_apps;
+        current_state_.atrace_categories = expected_categories;
+      }
+    }
   }
 
   return true;
@@ -564,29 +625,58 @@
 }
 
 void FtraceConfigMuxer::UpdateAtrace(const FtraceConfig& request) {
+  // We want to avoid poisoning current_state_.atrace_{categories, apps}
+  // if for some reason these args make atrace unhappy so we stash the
+  // union into temps and only update current_state_ if we successfully
+  // run atrace.
+
+  std::vector<std::string> combined_categories = request.atrace_categories();
+  UnionInPlace(current_state_.atrace_categories, &combined_categories);
+
+  std::vector<std::string> combined_apps = request.atrace_apps();
+  UnionInPlace(current_state_.atrace_apps, &combined_apps);
+
+  if (current_state_.atrace_on &&
+      combined_apps.size() == current_state_.atrace_apps.size() &&
+      combined_categories.size() == current_state_.atrace_categories.size()) {
+    return;
+  }
+
+  if (StartAtrace(combined_apps, combined_categories)) {
+    current_state_.atrace_categories = combined_categories;
+    current_state_.atrace_apps = combined_apps;
+    current_state_.atrace_on = true;
+  }
+}
+
+// static
+bool FtraceConfigMuxer::StartAtrace(
+    const std::vector<std::string>& apps,
+    const std::vector<std::string>& categories) {
   PERFETTO_DLOG("Update atrace config...");
 
   std::vector<std::string> args;
   args.push_back("atrace");  // argv0 for exec()
   args.push_back("--async_start");
   args.push_back("--only_userspace");
-  for (const auto& category : request.atrace_categories())
+
+  for (const auto& category : categories)
     args.push_back(category);
-  if (!request.atrace_apps().empty()) {
+
+  if (!apps.empty()) {
     args.push_back("-a");
     std::string arg = "";
-    for (const auto& app : request.atrace_apps()) {
+    for (const auto& app : apps) {
       arg += app;
-      if (app != request.atrace_apps().back())
-        arg += ",";
+      arg += ",";
     }
+    arg.resize(arg.size() - 1);
     args.push_back(arg);
   }
 
-  if (RunAtrace(args))
-    current_state_.atrace_on = true;
-
-  PERFETTO_DLOG("...done");
+  bool result = RunAtrace(args);
+  PERFETTO_DLOG("...done (%s)", result ? "success" : "fail");
+  return result;
 }
 
 void FtraceConfigMuxer::DisableAtrace() {
@@ -594,8 +684,11 @@
 
   PERFETTO_DLOG("Stop atrace...");
 
-  if (RunAtrace({"atrace", "--async_stop", "--only_userspace"}))
+  if (RunAtrace({"atrace", "--async_stop", "--only_userspace"})) {
+    current_state_.atrace_categories.clear();
+    current_state_.atrace_apps.clear();
     current_state_.atrace_on = false;
+  }
 
   PERFETTO_DLOG("...done");
 }
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.h b/src/traced/probes/ftrace/ftrace_config_muxer.h
index 416a38b..16883c4 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.h
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.h
@@ -32,8 +32,13 @@
 // that data source's config.
 struct FtraceDataSourceConfig {
   FtraceDataSourceConfig(EventFilter _event_filter,
-                         CompactSchedConfig _compact_sched)
-      : event_filter(std::move(_event_filter)), compact_sched(_compact_sched) {}
+                         CompactSchedConfig _compact_sched,
+                         std::vector<std::string> _atrace_apps,
+                         std::vector<std::string> _atrace_categories)
+      : event_filter(std::move(_event_filter)),
+        compact_sched(_compact_sched),
+        atrace_apps(std::move(_atrace_apps)),
+        atrace_categories(std::move(_atrace_categories)) {}
 
   // The event filter allows to quickly check if a certain ftrace event with id
   // x is enabled for this data source.
@@ -41,6 +46,10 @@
 
   // Configuration of the optional compact encoding of scheduling events.
   const CompactSchedConfig compact_sched;
+
+  // Used only in Android for ATRACE_EVENT/os.Trace() userspace annotations.
+  std::vector<std::string> atrace_apps;
+  std::vector<std::string> atrace_categories;
 };
 
 // Ftrace is a bunch of globally modifiable persistent state.
@@ -103,11 +112,17 @@
   }
 
  private:
+  static bool StartAtrace(const std::vector<std::string>& apps,
+                          const std::vector<std::string>& categories);
+
   struct FtraceState {
     EventFilter ftrace_events;
+    // Used only in Android for ATRACE_EVENT/os.Trace() userspace
+    std::vector<std::string> atrace_apps;
+    std::vector<std::string> atrace_categories;
+    size_t cpu_buffer_size_pages = 0;
     bool tracing_on = false;
     bool atrace_on = false;
-    size_t cpu_buffer_size_pages = 0;
   };
 
   FtraceConfigMuxer(const FtraceConfigMuxer&) = delete;
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 890e07a..0b069c2 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -509,7 +509,7 @@
       atrace,
       RunAtrace(ElementsAreArray(
           {"atrace", "--async_start", "--only_userspace", "-a",
-           "com.google.android.gms.persistent,com.google.android.gms"})))
+           "com.google.android.gms,com.google.android.gms.persistent"})))
       .WillOnce(Return(true));
 
   FtraceConfigId id = model.SetupConfig(config);
@@ -526,6 +526,211 @@
   ASSERT_TRUE(model.RemoveConfig(id));
 }
 
+TEST_F(FtraceConfigMuxerTest, AtraceMultipleConfigs) {
+  NiceMock<MockFtraceProcfs> ftrace;
+  MockRunAtrace atrace;
+
+  FtraceConfig config_a = CreateFtraceConfig({});
+  *config_a.add_atrace_apps() = "app_a";
+  *config_a.add_atrace_categories() = "cat_a";
+
+  FtraceConfig config_b = CreateFtraceConfig({});
+  *config_b.add_atrace_apps() = "app_b";
+  *config_b.add_atrace_categories() = "cat_b";
+
+  FtraceConfig config_c = CreateFtraceConfig({});
+  *config_c.add_atrace_apps() = "app_c";
+  *config_c.add_atrace_categories() = "cat_c";
+
+  FtraceConfigMuxer model(&ftrace, table_.get());
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                                  "--only_userspace", "cat_a",
+                                                  "-a", "app_a"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_a = model.SetupConfig(config_a);
+  ASSERT_TRUE(id_a);
+
+  EXPECT_CALL(
+      atrace,
+      RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
+                                  "cat_a", "cat_b", "-a", "app_a,app_b"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_b = model.SetupConfig(config_b);
+  ASSERT_TRUE(id_b);
+
+  EXPECT_CALL(atrace,
+              RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                          "--only_userspace", "cat_a", "cat_b",
+                                          "cat_c", "-a", "app_a,app_b,app_c"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_c = model.SetupConfig(config_c);
+  ASSERT_TRUE(id_c);
+
+  EXPECT_CALL(
+      atrace,
+      RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
+                                  "cat_a", "cat_c", "-a", "app_a,app_c"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_b));
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                                  "--only_userspace", "cat_c",
+                                                  "-a", "app_c"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_a));
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
+                          {"atrace", "--async_stop", "--only_userspace"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_c));
+}
+
+TEST_F(FtraceConfigMuxerTest, AtraceFailedConfig) {
+  NiceMock<MockFtraceProcfs> ftrace;
+  MockRunAtrace atrace;
+
+  FtraceConfig config_a = CreateFtraceConfig({});
+  *config_a.add_atrace_apps() = "app_1";
+  *config_a.add_atrace_apps() = "app_2";
+  *config_a.add_atrace_categories() = "cat_1";
+  *config_a.add_atrace_categories() = "cat_2";
+
+  FtraceConfig config_b = CreateFtraceConfig({});
+  *config_b.add_atrace_apps() = "app_fail";
+  *config_b.add_atrace_categories() = "cat_fail";
+
+  FtraceConfig config_c = CreateFtraceConfig({});
+  *config_c.add_atrace_apps() = "app_1";
+  *config_c.add_atrace_apps() = "app_3";
+  *config_c.add_atrace_categories() = "cat_1";
+  *config_c.add_atrace_categories() = "cat_3";
+
+  FtraceConfigMuxer model(&ftrace, table_.get());
+
+  EXPECT_CALL(
+      atrace,
+      RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
+                                  "cat_1", "cat_2", "-a", "app_1,app_2"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_a = model.SetupConfig(config_a);
+  ASSERT_TRUE(id_a);
+
+  EXPECT_CALL(atrace,
+              RunAtrace(ElementsAreArray(
+                  {"atrace", "--async_start", "--only_userspace", "cat_1",
+                   "cat_2", "cat_fail", "-a", "app_1,app_2,app_fail"})))
+      .WillOnce(Return(false));
+  FtraceConfigId id_b = model.SetupConfig(config_b);
+  ASSERT_TRUE(id_b);
+
+  EXPECT_CALL(atrace,
+              RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                          "--only_userspace", "cat_1", "cat_2",
+                                          "cat_3", "-a", "app_1,app_2,app_3"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_c = model.SetupConfig(config_c);
+  ASSERT_TRUE(id_c);
+
+  EXPECT_CALL(
+      atrace,
+      RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
+                                  "cat_1", "cat_2", "-a", "app_1,app_2"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_c));
+
+  // Removing the config we failed to enable doesn't change the atrace state
+  // so we don't expect a call here.
+  ASSERT_TRUE(model.RemoveConfig(id_b));
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
+                          {"atrace", "--async_stop", "--only_userspace"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_a));
+}
+
+TEST_F(FtraceConfigMuxerTest, AtraceDuplicateConfigs) {
+  NiceMock<MockFtraceProcfs> ftrace;
+  MockRunAtrace atrace;
+
+  FtraceConfig config_a = CreateFtraceConfig({});
+  *config_a.add_atrace_apps() = "app_1";
+  *config_a.add_atrace_categories() = "cat_1";
+
+  FtraceConfig config_b = CreateFtraceConfig({});
+  *config_b.add_atrace_apps() = "app_1";
+  *config_b.add_atrace_categories() = "cat_1";
+
+  FtraceConfigMuxer model(&ftrace, table_.get());
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                                  "--only_userspace", "cat_1",
+                                                  "-a", "app_1"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_a = model.SetupConfig(config_a);
+  ASSERT_TRUE(id_a);
+
+  FtraceConfigId id_b = model.SetupConfig(config_b);
+  ASSERT_TRUE(id_b);
+
+  ASSERT_TRUE(model.RemoveConfig(id_a));
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
+                          {"atrace", "--async_stop", "--only_userspace"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_b));
+}
+
+TEST_F(FtraceConfigMuxerTest, AtraceAndFtraceConfigs) {
+  NiceMock<MockFtraceProcfs> ftrace;
+  MockRunAtrace atrace;
+
+  FtraceConfig config_a = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
+
+  FtraceConfig config_b = CreateFtraceConfig({"sched/sched_switch"});
+  *config_b.add_atrace_categories() = "b";
+
+  FtraceConfig config_c = CreateFtraceConfig({"sched/sched_switch"});
+
+  FtraceConfig config_d = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
+  *config_d.add_atrace_categories() = "d";
+
+  FtraceConfigMuxer model(&ftrace, table_.get());
+
+  FtraceConfigId id_a = model.SetupConfig(config_a);
+  ASSERT_TRUE(id_a);
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                                  "--only_userspace", "b"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_b = model.SetupConfig(config_b);
+  ASSERT_TRUE(id_b);
+
+  FtraceConfigId id_c = model.SetupConfig(config_c);
+  ASSERT_TRUE(id_c);
+
+  EXPECT_CALL(atrace,
+              RunAtrace(ElementsAreArray(
+                  {"atrace", "--async_start", "--only_userspace", "b", "d"})))
+      .WillOnce(Return(true));
+  FtraceConfigId id_d = model.SetupConfig(config_d);
+  ASSERT_TRUE(id_d);
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
+                                                  "--only_userspace", "b"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_d));
+
+  ASSERT_TRUE(model.RemoveConfig(id_c));
+
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
+                          {"atrace", "--async_stop", "--only_userspace"})))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.RemoveConfig(id_b));
+
+  ASSERT_TRUE(model.RemoveConfig(id_a));
+}
+
 TEST_F(FtraceConfigMuxerTest, SetupClockForTesting) {
   MockFtraceProcfs ftrace;
   FtraceConfig config;
diff --git a/src/traced/probes/ftrace/ftrace_data_source.cc b/src/traced/probes/ftrace/ftrace_data_source.cc
index f576f16..f28a491 100644
--- a/src/traced/probes/ftrace/ftrace_data_source.cc
+++ b/src/traced/probes/ftrace/ftrace_data_source.cc
@@ -36,12 +36,12 @@
     : ProbesDataSource(session_id, kTypeId),
       config_(config),
       writer_(std::move(writer)),
-      controller_weak_(std::move(controller_weak)){};
+      controller_weak_(std::move(controller_weak)) {}
 
 FtraceDataSource::~FtraceDataSource() {
   if (controller_weak_)
     controller_weak_->RemoveDataSource(this);
-};
+}
 
 void FtraceDataSource::Initialize(
     FtraceConfigId config_id,
diff --git a/src/traced/probes/ftrace/ftrace_metadata.h b/src/traced/probes/ftrace/ftrace_metadata.h
index de9d89a..8294f25 100644
--- a/src/traced/probes/ftrace/ftrace_metadata.h
+++ b/src/traced/probes/ftrace/ftrace_metadata.h
@@ -23,8 +23,8 @@
 
 #include <bitset>
 
+#include "perfetto/base/flat_set.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/flat_set.h"
 #include "perfetto/ext/traced/data_source_types.h"
 
 namespace perfetto {
diff --git a/src/traced/probes/ftrace/kallsyms/BUILD.gn b/src/traced/probes/ftrace/kallsyms/BUILD.gn
new file mode 100644
index 0000000..0c42b98
--- /dev/null
+++ b/src/traced/probes/ftrace/kallsyms/BUILD.gn
@@ -0,0 +1,52 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../../gn/test.gni")
+
+source_set("kallsyms") {
+  deps = [
+    "../../../../../gn:default_deps",
+    "../../../../../include/perfetto/protozero",
+    "../../../../base",
+  ]
+  sources = [
+    "kernel_symbol_map.cc",
+    "kernel_symbol_map.h",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":kallsyms",
+    "../../../../../gn:default_deps",
+    "../../../../../gn:gtest_and_gmock",
+    "../../../../base",
+  ]
+  sources = [ "kernel_symbol_map_unittest.cc" ]
+}
+
+if (enable_perfetto_benchmarks) {
+  source_set("benchmarks") {
+    testonly = true
+    deps = [
+      ":kallsyms",
+      "../../../../../gn:benchmark",
+      "../../../../../gn:default_deps",
+      "../../../../base",
+      "../../../../base:test_support",
+    ]
+    sources = [ "kernel_symbol_map_benchmark.cc" ]
+  }
+}
diff --git a/src/traced/probes/ftrace/kallsyms/kernel_symbol_map.cc b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map.cc
new file mode 100644
index 0000000..f2d55f0
--- /dev/null
+++ b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map.cc
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2019 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/traced/probes/ftrace/kallsyms/kernel_symbol_map.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/metatrace.h"
+#include "perfetto/ext/base/paged_memory.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/protozero/proto_utils.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <utility>
+
+namespace perfetto {
+
+// On a Pixel 3 this gives an avg. lookup time of 600 ns and a memory usage
+// of 1.1 MB for 65k symbols. See go/kallsyms-parser-bench.
+size_t KernelSymbolMap::kSymIndexSampling = 16;
+size_t KernelSymbolMap::kTokenIndexSampling = 4;
+
+namespace {
+
+using TokenId = KernelSymbolMap::TokenTable::TokenId;
+constexpr size_t kSymNameMaxLen = 128;
+
+// Reads a kallsyms file in blocks of 4 pages each and decode its lines using
+// a simple FSM. Calls the passed lambda for each valid symbol.
+// It skips undefined symbols and other useless stuff.
+template <typename Lambda /* void(uint64_t, const char*) */>
+void ForEachSym(const std::string& kallsyms_path, Lambda fn) {
+  base::ScopedFile fd = base::OpenFile(kallsyms_path.c_str(), O_RDONLY);
+  if (!fd) {
+    PERFETTO_PLOG("Cannot open %s", kallsyms_path.c_str());
+    return;
+  }
+
+  // /proc/kallsyms looks as follows:
+  // 0000000000026a80 A bpf_trace_sds
+  //
+  // ffffffffc03a6000 T cpufreq_gov_powersave_init<TAB> [cpufreq_powersave]
+  // ffffffffc035d000 T cpufreq_gov_userspace_init<TAB> [cpufreq_userspace]
+  //
+  // We parse it with a state machine that has four states, one for each column.
+  // We don't care about the part in the square brackets and ignore everything
+  // after the symbol name.
+
+  static constexpr size_t kBufSize = 16 * 1024;
+  base::PagedMemory buffer = base::PagedMemory::Allocate(kBufSize);
+  enum { kSymAddr, kSymType, kSymName, kEatRestOfLine } state = kSymAddr;
+  uint64_t sym_addr = 0;
+  char sym_type = '\0';
+  char sym_name[kSymNameMaxLen + 1];
+  size_t sym_name_len = 0;
+  for (;;) {
+    char* buf = static_cast<char*>(buffer.Get());
+    auto rsize = PERFETTO_EINTR(read(*fd, buf, kBufSize));
+    if (rsize < 0) {
+      PERFETTO_PLOG("read(%s) failed", kallsyms_path.c_str());
+      return;
+    }
+    if (rsize == 0)
+      return;  // EOF
+    for (size_t i = 0; i < static_cast<size_t>(rsize); i++) {
+      char c = buf[i];
+      const bool is_space = c == ' ' || c == '\t';
+      switch (state) {
+        case kSymAddr:
+          if (c >= '0' && c <= '9') {
+            sym_addr = (sym_addr << 4) | static_cast<uint8_t>(c - '0');
+          } else if (c >= 'a' && c <= 'f') {
+            sym_addr = (sym_addr << 4) | static_cast<uint8_t>(c - 'a' + 10);
+          } else if (is_space) {
+            state = kSymType;
+          } else if (c == '\0') {
+            return;
+          } else {
+            PERFETTO_ELOG("kallsyms parser error: chr 0x%x @ off=%zu", c, i);
+            return;
+          }
+          break;
+
+        case kSymType:
+          if (is_space)
+            break;  // Eat leading spaces.
+          sym_type = c;
+          state = kSymName;
+          sym_name_len = 0;
+          break;
+
+        case kSymName:
+          if (is_space && sym_name_len == 0)
+            break;  // Eat leading spaces.
+          if (c && c != '\n' && !is_space && sym_name_len < kSymNameMaxLen) {
+            sym_name[sym_name_len++] = c;
+            break;
+          }
+          sym_name[sym_name_len] = '\0';
+          fn(sym_addr, sym_type, sym_name);
+          sym_addr = 0;
+          sym_type = '\0';
+          state = c == '\n' ? kSymAddr : kEatRestOfLine;
+          break;
+
+        case kEatRestOfLine:
+          if (c == '\n')
+            state = kSymAddr;
+          break;
+      }  // switch(state)
+    }    // for (char in buf)
+  }      // for (read chunk)
+}
+
+// Splits a symbol name into tokens using '_' as a separator, calling the passed
+// lambda for each token. It splits tokens in a way that allows the original
+// string to be rebuilt as-is by re-joining using a '_' between each token.
+// For instance:
+// _fo_a_b      ->  ["", fo, a, b]
+// __fo_a_b     ->  [_, fo, a, b]
+// __fo_a_b_    ->  [_, fo, a, b, ""]
+// __fo_a_b____ ->  [_, fo, a, b, ___]
+template <typename Lambda /* void(base::StringView) */>
+void Tokenize(const char* name, Lambda fn) {
+  const char* tok_start = name;
+  bool is_start_of_token = true;
+  bool tok_is_sep = false;
+  for (const char* ptr = name;; ptr++) {
+    const char c = *ptr;
+    if (is_start_of_token) {
+      tok_is_sep = *tok_start == '_';  // Deals with tokens made of '_'s.
+      is_start_of_token = false;
+    }
+    // Scan until either the end of string or the next character (which is a '_'
+    // in nominal cases, or anything != '_' for tokens made by 1+ '_').
+    if (c == '\0' || (!tok_is_sep && c == '_') || (tok_is_sep && c != '_')) {
+      size_t tok_len = static_cast<size_t>(ptr - tok_start);
+      if (tok_is_sep && c != '\0')
+        --tok_len;
+      fn(base::StringView(tok_start, tok_len));
+      if (c == '\0')
+        return;
+      tok_start = tok_is_sep ? ptr : ptr + 1;
+      is_start_of_token = true;
+    }
+  }
+}
+
+}  // namespace
+
+KernelSymbolMap::TokenTable::TokenTable() {
+  // Insert a null token as id 0. We can't just add "" because the empty string
+  // is special-cased and doesn't insert an actual token. So we push a string of
+  // size one that contains only the null character instead.
+  char null_tok = 0;
+  Add(std::string(&null_tok, 1));
+}
+
+KernelSymbolMap::TokenTable::~TokenTable() = default;
+
+// Adds a new token to the db. Does not dedupe identical token (with the
+// exception of the empty string). The caller has to deal with that.
+// Supports only ASCII characters in the range [1, 127].
+// The last character of the token will have the MSB set.
+TokenId KernelSymbolMap::TokenTable::Add(const std::string& token) {
+  const size_t token_size = token.size();
+  if (token_size == 0)
+    return 0;
+  TokenId id = num_tokens_++;
+
+  const size_t buf_size_before_insertion = buf_.size();
+  if (id % kTokenIndexSampling == 0)
+    index_.emplace_back(buf_size_before_insertion);
+
+  const size_t prev_size = buf_.size();
+  buf_.resize(prev_size + token_size);
+  char* tok_wptr = &buf_[prev_size];
+  for (size_t i = 0; i < token_size - 1; i++) {
+    PERFETTO_DCHECK((token.at(i) & 0x80) == 0);  // |token| must be ASCII only.
+    *(tok_wptr++) = token.at(i) & 0x7f;
+  }
+  *(tok_wptr++) = token.at(token_size - 1) | 0x80;
+  PERFETTO_DCHECK(tok_wptr == &buf_[buf_.size()]);
+  return id;
+}
+
+// NOTE: the caller need to mask the returned chars with 0x7f. The last char of
+// the StringView will have its MSB set (it's used as a EOF char internally).
+base::StringView KernelSymbolMap::TokenTable::Lookup(TokenId id) {
+  if (id == 0)
+    return base::StringView();
+  if (id > num_tokens_)
+    return base::StringView("<error>");
+  // We don't know precisely where the id-th token starts in the buffer. We
+  // store only one position every kTokenIndexSampling. From there, the token
+  // can be found with a linear scan of at most kTokenIndexSampling steps.
+  size_t index_off = id / kTokenIndexSampling;
+  PERFETTO_DCHECK(index_off < index_.size());
+  TokenId cur_id = static_cast<TokenId>(index_off * kTokenIndexSampling);
+  uint32_t begin = index_[index_off];
+  PERFETTO_DCHECK(begin == 0 || buf_[begin - 1] & 0x80);
+  const size_t buf_size = buf_.size();
+  for (uint32_t off = begin; off < buf_size; ++off) {
+    // Advance |off| until the end of the token (which has the MSB set).
+    if ((buf_[off] & 0x80) == 0)
+      continue;
+    if (cur_id == id)
+      return base::StringView(&buf_[begin], off - begin + 1);
+    ++cur_id;
+    begin = off + 1;
+  }
+  return base::StringView();
+}
+
+size_t KernelSymbolMap::Parse(const std::string& kallsyms_path) {
+  PERFETTO_METATRACE_SCOPED(TAG_FTRACE, KALLSYMS_PARSE);
+  using SymAddr = uint64_t;
+
+  struct TokenInfo {
+    uint32_t count = 0;
+    TokenId id = 0;
+  };
+
+  // Note if changing the container: the code below relies on stable iterators.
+  using TokenMap = std::map<std::string, TokenInfo>;
+  using TokenMapPtr = TokenMap::value_type*;
+  TokenMap tokens;
+
+  // Keep the (ordered) list of tokens for each symbol.
+  std::multimap<SymAddr, TokenMapPtr> symbols;
+
+  ForEachSym(kallsyms_path, [&](SymAddr addr, char type, const char* name) {
+    if (addr == 0 || (type != 't' && type != 'T') || name[0] == '$') {
+      return;
+    }
+
+    // Split each symbol name in tokens, using '_' as a separator (so that
+    // "foo_bar" -> ["foo", "bar"]). For each token hash:
+    // 1. Keep track of the frequency of each token.
+    // 2. Keep track of the list of token hashes for each symbol.
+    Tokenize(name, [&tokens, &symbols, addr](base::StringView token) {
+      // Strip the .cfi part if present.
+      if (token.substr(token.size() - 4) == ".cfi")
+        token = token.substr(0, token.size() - 4);
+      auto it_and_ins = tokens.emplace(token.ToStdString(), TokenInfo{});
+      it_and_ins.first->second.count++;
+      symbols.emplace(addr, &*it_and_ins.first);
+    });
+  });
+
+  // At this point we have broken down each symbol into a set of token hashes.
+  // Now generate the token ids, putting high freq tokens first, so they use
+  // only one byte to varint encode.
+
+  // This block limits the lifetime of |tokens_by_freq|.
+  {
+    std::vector<TokenMapPtr> tokens_by_freq;
+    tokens_by_freq.resize(tokens.size());
+    size_t tok_idx = 0;
+    for (auto& kv : tokens)
+      tokens_by_freq[tok_idx++] = &kv;
+
+    auto comparer = [](TokenMapPtr a, TokenMapPtr b) {
+      PERFETTO_DCHECK(a && b);
+      return b->second.count < a->second.count;
+    };
+    std::sort(tokens_by_freq.begin(), tokens_by_freq.end(), comparer);
+    for (TokenMapPtr tinfo : tokens_by_freq) {
+      tinfo->second.id = tokens_.Add(tinfo->first);
+    }
+  }
+  tokens_.shrink_to_fit();
+
+  buf_.resize(2 * 1024 * 1024);  // Based on real-word observations.
+  base_addr_ = symbols.empty() ? 0 : symbols.begin()->first;
+  SymAddr prev_sym_addr = base_addr_;
+  uint8_t* wptr = buf_.data();
+
+  for (auto it = symbols.begin(); it != symbols.end();) {
+    const SymAddr sym_addr = it->first;
+
+    // Find the iterator to the first token of the next symbol (or the end).
+    auto sym_start = it;
+    auto sym_end = it;
+    while (sym_end != symbols.end() && sym_end->first == sym_addr)
+      ++sym_end;
+
+    // The range [sym_start, sym_end) has all the tokens for the current symbol.
+    uint32_t size_before = static_cast<uint32_t>(wptr - buf_.data());
+
+    // Make sure there is enough headroom to write the symbol.
+    if (buf_.size() - size_before < 1024) {
+      buf_.resize(buf_.size() + 32768);
+      wptr = buf_.data() + size_before;
+    }
+
+    uint32_t sym_rel_addr = static_cast<uint32_t>(sym_addr - base_addr_);
+    const size_t sym_num = num_syms_++;
+    if (sym_num % kSymIndexSampling == 0)
+      index_.emplace_back(std::make_pair(sym_rel_addr, size_before));
+    PERFETTO_DCHECK(sym_addr >= prev_sym_addr);
+    uint32_t delta = static_cast<uint32_t>(sym_addr - prev_sym_addr);
+    wptr = protozero::proto_utils::WriteVarInt(delta, wptr);
+    // Append all the token ids.
+    for (it = sym_start; it != sym_end;) {
+      PERFETTO_DCHECK(it->first == sym_addr);
+      TokenId token_id = it->second->second.id << 1;
+      ++it;
+      token_id |= (it == sym_end) ? 1 : 0;  // Last one has LSB set to 1.
+      wptr = protozero::proto_utils::WriteVarInt(token_id, wptr);
+    }
+    prev_sym_addr = sym_addr;
+  }  // for (symbols)
+
+  buf_.resize(static_cast<size_t>(wptr - buf_.data()));
+  buf_.shrink_to_fit();
+
+  PERFETTO_DLOG(
+      "Loaded %zu kalllsyms entries. Mem usage: %zu B (addresses) + %zu B "
+      "(tokens), total: %zu B",
+      num_syms_, addr_bytes(), tokens_.size_bytes(), size_bytes());
+
+  return num_syms_;
+}
+
+std::string KernelSymbolMap::Lookup(uint64_t sym_addr) {
+  if (index_.empty() || sym_addr < base_addr_)
+    return "";
+
+  // First find the highest symbol address <= sym_addr.
+  // Start with a binary search using the sparse index.
+
+  const uint32_t sym_rel_addr = static_cast<uint32_t>(sym_addr - base_addr_);
+  auto it = std::upper_bound(index_.cbegin(), index_.cend(),
+                             std::make_pair(sym_rel_addr, 0u));
+  if (it != index_.cbegin())
+    --it;
+
+  // Then continue with a linear scan (of at most kSymIndexSampling steps).
+  uint32_t addr = it->first;
+  uint32_t off = it->second;
+  const uint8_t* rdptr = &buf_[off];
+  const uint8_t* const buf_end = &buf_[buf_.size()];
+  bool parsing_addr = true;
+  const uint8_t* next_rdptr = nullptr;
+  for (bool is_first_addr = true;; is_first_addr = false) {
+    uint64_t v = 0;
+    const auto* prev_rdptr = rdptr;
+    rdptr = protozero::proto_utils::ParseVarInt(rdptr, buf_end, &v);
+    if (rdptr == prev_rdptr)
+      break;
+    if (parsing_addr) {
+      addr += is_first_addr ? 0 : static_cast<uint32_t>(v);
+      parsing_addr = false;
+      if (addr > sym_rel_addr)
+        break;
+      next_rdptr = rdptr;
+    } else {
+      // This is a token. Wait for the EOF maker.
+      parsing_addr = (v & 1) == 1;
+    }
+  }
+
+  if (!next_rdptr)
+    return "";
+
+  // The address has been found. Now rejoin the tokens to form the symbol name.
+
+  rdptr = next_rdptr;
+  std::string sym_name;
+  sym_name.reserve(kSymNameMaxLen);
+  for (bool eof = false, is_first_token = true; !eof; is_first_token = false) {
+    uint64_t v = 0;
+    const auto* old = rdptr;
+    rdptr = protozero::proto_utils::ParseVarInt(rdptr, buf_end, &v);
+    if (rdptr == old)
+      break;
+    eof = v & 1;
+    base::StringView token = tokens_.Lookup(static_cast<TokenId>(v >> 1));
+    if (!is_first_token)
+      sym_name.push_back('_');
+    for (size_t i = 0; i < token.size(); i++)
+      sym_name.push_back(token.at(i) & 0x7f);
+  }
+  return sym_name;
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/kallsyms/kernel_symbol_map.h b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map.h
new file mode 100644
index 0000000..c13546a
--- /dev/null
+++ b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2019 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_TRACED_PROBES_FTRACE_KALLSYMS_KERNEL_SYMBOL_MAP_H_
+#define SRC_TRACED_PROBES_FTRACE_KALLSYMS_KERNEL_SYMBOL_MAP_H_
+
+#include <stdint.h>
+#include <array>
+#include <forward_list>
+#include <vector>
+
+namespace perfetto {
+
+namespace base {
+class StringView;
+}
+
+// A parser and memory-efficient container for /proc/kallsyms.
+// It can store a full kernel symbol table in ~1.2MB of memory and perform fast
+// lookups using logarithmic binary searches + bounded linear scans.
+//
+// /proc/kallsyms is a ~10 MB text file that contains the map of kernel symbols,
+// as follows:
+// ffffff8f77682f8c t el0_sync_invalid
+// ffffff8f77683060 t el0_irq_invalid
+// ...
+// In a typipcal Android kernel, it consists of 213K lines. Out of these, only
+// 116K are interesting for the sake of symbolizing kernel functions, the rest
+// are .rodata (variables), weak or other useless symbols.
+// Still, even keeping around 116K pointers would require 116K * 8 ~= 1 MB of
+// memory, without accounting for any strings for the symbols names.
+// The SUM(str.len) for the 116K symbol names adds up to 2.7 MB (without
+// counting their addresses).
+// However consider the following:
+// - Symbol addresses are mostly contiguous. Modulo the initial KASLR loading
+//   address, most symbols are few hundreds bytes apart from each other.
+// - Symbol names are made of tokens that are quite frequent (token: the result
+//   of name.split('_')). If we tokenize the 2.7 MB of strings, the resulting
+//   SUM(distinct_token.len) goes down 2.7MB -> 146 KB. This is because tokens
+//   like "get", "set" or "event" show up thousands of times.
+// - Symbol names are ASCII strings using only 7 out of 8 bits.
+//
+// In the light of this, the in-memory architecture of this data structure is
+// as follows:
+// We keep two tables around: (1) a token table and (2) a symbol table. Both
+// table are a flat byte vector with some sparse lookaside index to make lookups
+// faster and avoid full linear scans.
+//
+// Token table
+// -----------
+// The token table is a flat char buffer. Tokens are variable size (>0). Each
+// token is identified by its ordinality, so token id 3 is the 3rd token in
+// the table. All tokens are concatenated together.
+// Given the ASCII encoding, the MSB is used as a terminator. So instead of
+// wasting an extra NUL byte for each string, the last char of each token has
+// the MSB set.
+// Furthermore, a lookaside index stores the offset of tokens (i.e. Token N
+// starts at offset O in the buffer) to allow fast lookups. In order to avoid
+// wasting too much memory, the index is sparse and track the offsets of only
+// one every kTokenIndexSamplinig tokens.
+// When looking up a token ID N, the table seeks at the offset of the closest
+// token <= N, and then scans linearly the next (at most kTokenIndexSamplinig)
+// tokens, counting the MSBs found, until the right token id is found.
+// buf:   set*get*kernel*load*fpsimd*return*wrapper*el0*skip*sync*neon*bit*aes
+//        ^                   ^                         ^
+//        |                   |                         |
+// index: 0@0                 4@15                      8@21
+
+// Symbol table
+// ------------
+// The symbol table is a flat char buffer that stores for each symbol: its
+// address + the list of token indexes in the token table. The main caveats are
+// that:
+// - Symbol addresses are delta encoded (delta from prev symbol's addr).
+// - Both delta addresses and token indexes are var-int encoded.
+// - The LSB of token indexes is used as EOF marker (i.e. the next varint is
+//   the delta-addr for the next symbol). This time the LSB is used because of
+//   the varint encoding.
+// At parsing time symbols are ordered by address and tokens are sorted by
+// frequency, so that the top used 64 tokens can be represented with 1 byte.
+// (Rationale for 64: 1 byte = 8 bits. The MSB bit of each byte is used for the
+// varint encoding, the LSB bit of each number is used as end-of-tokens marker.
+// There are 6 bits left -> 64 indexes can be represented using one byte).
+// In summary the symbol table looks as follows:
+//
+// Base address: 0xbeef0000
+// Symbol buffer:
+// 0 1|0  4|0  6|1    // 0xbeef0000: 1,4,6 -> get_fpsimd_wrapper
+// 8 7|0  3|1         // 0xbeef0008: 7,3   -> el0_load
+// ...
+// Like in the case of the token table, a lookaside index keeps track of the
+// offset of one every kSymIndexSamplinig addresses.
+// The Lookup(ADDR) function operates as follows:
+// 1. Performs a logarithmic binary search in the symbols index, finding the
+//    offset of the closest addres <= ADDR.
+// 2. Skip over at most kSymIndexSamplinig until the symbol is found.
+// 3. For each token index, lookup the corresponding token string and
+//    concatenate them to build the symbol name.
+
+class KernelSymbolMap {
+ public:
+  // The two constants below are changeable only for the benchmark use.
+  // Trades off size of the root |index_| vs worst-case linear scans size.
+  // A higher number makes the index more sparse.
+  static size_t kSymIndexSampling;
+
+  // Trades off size of the TokenTable |index_| vs worst-case linear scans size.
+  static size_t kTokenIndexSampling;
+
+  // Parses a kallsyms file. Returns the number of valid symbols decoded.
+  size_t Parse(const std::string& kallsyms_path);
+
+  // Looks up the closest symbol (i.e. the one with the highest address <=
+  // |addr|) from its absolute 64-bit address.
+  // Returns an empty string if the symbol is not found (which can happen only
+  // if the passed |addr| is < min(addr)).
+  std::string Lookup(uint64_t addr);
+
+  // Returns the numberr of valid symbols decoded.
+  size_t num_syms() const { return num_syms_; }
+
+  // Returns the size in bytes used by the adddress table (without counting
+  // the tokens).
+  size_t addr_bytes() const { return buf_.size() + index_.size() * 8; }
+
+  // Returns the total memory usage in bytes.
+  size_t size_bytes() const { return addr_bytes() + tokens_.size_bytes(); }
+
+  // Token table.
+  class TokenTable {
+   public:
+    using TokenId = uint32_t;
+    TokenTable();
+    ~TokenTable();
+    TokenId Add(const std::string&);
+    base::StringView Lookup(TokenId);
+    size_t size_bytes() const { return buf_.size() + index_.size() * 4; }
+
+    void shrink_to_fit() {
+      buf_.shrink_to_fit();
+      index_.shrink_to_fit();
+    }
+
+   private:
+    TokenId num_tokens_ = 0;
+
+    std::vector<char> buf_;  // Token buffer.
+
+    // The value i-th in the vector contains the offset (within |buf_|) of the
+    // (i * kTokenIndexSamplinig)-th token.
+    std::vector<uint32_t> index_;
+  };
+
+ private:
+  TokenTable tokens_;  // Token table.
+
+  uint64_t base_addr_ = 0;    // Address of the first symbol (after sorting).
+  size_t num_syms_ = 0;       // Number of valid symbols stored.
+  std::vector<uint8_t> buf_;  // Symbol buffer.
+
+  // The key is (address - base_addr_), the value is the byte offset in |buf_|
+  // where the symbol entry starts (i.e. the start of the varint that tells the
+  // delta from the previous symbol).
+  std::vector<std::pair<uint32_t /*rel_addr*/, uint32_t /*offset*/>> index_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FTRACE_KALLSYMS_KERNEL_SYMBOL_MAP_H_
diff --git a/src/traced/probes/ftrace/kallsyms/kernel_symbol_map_benchmark.cc b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map_benchmark.cc
new file mode 100644
index 0000000..cdd4e38
--- /dev/null
+++ b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map_benchmark.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2019 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 <random>
+#include <set>
+#include <unordered_set>
+
+#include <benchmark/benchmark.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/utils.h"
+#include "src/base/test/utils.h"
+#include "src/traced/probes/ftrace/kallsyms/kernel_symbol_map.h"
+
+namespace {
+
+bool IsBenchmarkFunctionalOnly() {
+  return getenv("BENCHMARK_FUNCTIONAL_TEST_ONLY") != nullptr;
+}
+
+void BenchmarkArgs(benchmark::internal::Benchmark* b) {
+  if (IsBenchmarkFunctionalOnly()) {
+    b->Ranges({{16, 16}, {16, 16}});
+  } else {
+    b->RangeMultiplier(2)->Ranges({{4, 512}, {4, 512}});
+  }
+}
+
+struct ExpectedSym {
+  uint64_t addr;
+  const char* name;
+};
+
+// This set of symbols has been chosen by randomly picking 40 random symbols
+// from the original kallsyms.
+ExpectedSym kExpectedSyms[] = {
+    {0xffffff8f79c0d978, "__map_memblock"},
+    {0xffffff8f78fddbb8, "smack_inode_getsecid"},
+    {0xffffff8f78fe43b4, "msm_smmu_set_attribute"},
+    {0xffffff8f79d23e20, "__initcall_41_dm_verity_init6"},
+    {0xffffff8f74206c5c, "sme_update_fast_transition_enabled"},
+    {0xffffff8f74878c8c, "tavil_hph_idle_detect_put"},
+    {0xffffff8f78fd7db0, "privileged_wrt_inode_uidgid"},
+    {0xffffff8f78ffe030, "__hrtimer_tasklet_trampoline"},
+    {0xffffff8f78fd86b0, "store_enable"},
+    {0xffffff8f78ffbcb8, "raw6_exit_net"},
+    {0xffffff8f78ffa6ec, "idProduct_show"},
+    {0xffffff8f78fd99c0, "perf_tp_event"},
+    {0xffffff8f78fe1468, "rpmh_tx_done"},
+    {0xffffff8f78fda274, "page_unlock_anon_vma_read"},
+    {0xffffff8f78ffedfc, "vmstat_period_ms_operations_open"},
+    {0xffffff8f78fe0148, "devm_gpio_request"},
+    {0xffffff8f77915028, "ctx_sched_out"},
+    {0xffffff8f77ccdc2c, "gcm_hash_crypt_remain_continue"},
+    {0xffffff8f790022ec, "loop_init"},
+    {0xffffff8f78ff0004, "pcim_release"},
+    {0xffffff8f78fe1d8c, "uart_close"},
+    {0xffffff8f78fda9d4, "pipe_lock"},
+    {0xffffff8f78e62c68, "local_bh_enable.117091"},
+    {0xffffff8f78fd918c, "fork_idle"},
+    {0xffffff8f78fe24c4, "drm_dp_downstream_debug"},
+    {0xffffff8f78ff41d0, "inet_addr_onlink"},
+    {0xffffff8f78fdf2d4, "idr_alloc"},
+    {0xffffff8f78ff073c, "fts_remove"},
+    {0xffffff8f78ffe294, "xfrm4_local_error"},
+    {0xffffff8f79001994, "cpu_feature_match_PMULL_init"},
+    {0xffffff8f78ff4740, "xfrm_state_find"},
+    {0xffffff8f78ff58b0, "inet_del_offload"},
+    {0xffffff8f742041ac, "csr_is_conn_state_connected_infra"},
+    {0xffffff8f78fe1fd4, "diag_add_client"},
+    {0xffffff8f78ffc000, "trace_raw_output_mm_vmscan_kswapd_sleep"},
+    {0xffffff8f78fe6388, "scsi_queue_insert"},
+    {0xffffff8f78fdd480, "selinux_sb_clone_mnt_opts"},
+    {0xffffff8f78fe0e9c, "clk_fixed_rate_recalc_rate"},
+    {0xffffff8f78fedaec, "cap_inode_killpriv"},
+    {0xffffff8f79002b64, "audio_amrwb_init"},
+};
+
+}  // namespace
+
+static void BM_KallSyms(benchmark::State& state) {
+  perfetto::KernelSymbolMap::kTokenIndexSampling =
+      static_cast<size_t>(state.range(0));
+  perfetto::KernelSymbolMap::kSymIndexSampling =
+      static_cast<size_t>(state.range(1));
+  perfetto::KernelSymbolMap kallsyms;
+
+  // Don't run the benchmark on the CI as it requires pushing all test data,
+  // which slows down significantly the CI.
+  const bool skip = IsBenchmarkFunctionalOnly();
+  if (!skip) {
+    kallsyms.Parse(perfetto::base::GetTestDataPath("test/data/kallsyms.txt"));
+  }
+
+  for (auto _ : state) {
+    for (size_t i = 0; i < perfetto::base::ArraySize(kExpectedSyms); i++) {
+      const auto& exp = kExpectedSyms[i];
+      PERFETTO_CHECK(skip || kallsyms.Lookup(exp.addr) == exp.name);
+    }
+  }
+
+  state.counters["mem"] = kallsyms.size_bytes();
+}
+
+BENCHMARK(BM_KallSyms)->Apply(BenchmarkArgs);
diff --git a/src/traced/probes/ftrace/kallsyms/kernel_symbol_map_unittest.cc b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map_unittest.cc
new file mode 100644
index 0000000..942820a
--- /dev/null
+++ b/src/traced/probes/ftrace/kallsyms/kernel_symbol_map_unittest.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 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/traced/probes/ftrace/kallsyms/kernel_symbol_map.h"
+
+#include <inttypes.h>
+
+#include <random>
+#include <unordered_map>
+
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/temp_file.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace {
+
+using TokenId = KernelSymbolMap::TokenTable::TokenId;
+
+std::string Lookup(KernelSymbolMap::TokenTable& tokens, TokenId id) {
+  base::StringView sv = tokens.Lookup(id);
+  std::string str;
+  str.reserve(sv.size() + 1);
+  for (const char c : sv)
+    str.push_back(c & 0x7f);
+  return str;
+}
+
+TEST(KernelSymbolMapTest, TokenTable) {
+  KernelSymbolMap::TokenTable tokens;
+  ASSERT_EQ(tokens.Add("a"), 1u);
+  ASSERT_EQ(tokens.Add("bb"), 2u);
+  ASSERT_EQ(tokens.Add("ccc"), 3u);
+  ASSERT_EQ(tokens.Add("foobar"), 4u);
+  ASSERT_EQ(tokens.Add("foobaz"), 5u);
+  ASSERT_EQ(tokens.Add("_"), 6u);
+  ASSERT_EQ(Lookup(tokens, 0), "");
+  ASSERT_EQ(Lookup(tokens, 1), "a");
+  ASSERT_EQ(Lookup(tokens, 2), "bb");
+  ASSERT_EQ(Lookup(tokens, 3), "ccc");
+  ASSERT_EQ(Lookup(tokens, 4), "foobar");
+  ASSERT_EQ(Lookup(tokens, 5), "foobaz");
+  ASSERT_EQ(Lookup(tokens, 6), "_");
+  ASSERT_EQ(Lookup(tokens, 0), "");
+  ASSERT_EQ(Lookup(tokens, 42), "<error>");
+}
+
+TEST(KernelSymbolMapTest, ManyTokens) {
+  KernelSymbolMap::TokenTable tokens;
+  std::unordered_map<TokenId, std::string> tok_map;
+  static std::minstd_rand rng(0);
+  for (int rep = 0; rep < 10000; rep++) {
+    static const size_t kNameMax = 128;
+    std::string sym_name;
+    sym_name.reserve(kNameMax);
+    size_t len = 1 + (rng() % kNameMax);
+    for (size_t j = 0; j < len; j++)
+      sym_name.push_back(rng() & 0x7f);
+    TokenId id = tokens.Add(sym_name);
+    ASSERT_EQ(tok_map.count(id), 0u);
+    tok_map[id] = sym_name;
+  }
+  for (const auto& kv : tok_map) {
+    ASSERT_EQ(Lookup(tokens, kv.first), kv.second);
+  }
+}
+
+TEST(KernelSymbolMapTest, EdgeCases) {
+  base::TempFile tmp = base::TempFile::Create();
+  static const char kContents[] = R"(ffffff8f73e2fa10 t one
+ffffff8f73e2fa20 t two_
+ffffff8f73e2fa30 t _three  [module_name_ignored]
+ffffff8f73e2fa40 x ignore
+ffffff8f73e2fa40 t _fo_ur_
+ffffff8f73e2fa50 t _five__.cfi
+ffffff8f73e2fa60 t __si__x__
+ffffff8f73e2fa70 t ___se___v_e__n___
+ffffff8f73e2fa80 t _eight_omg_this_name_is_so_loooooooooooooooooooooooooooooooooooong_should_be_truncated_exactly_here_because_this_is_the_128_char_TRUNCATED_HERE
+ffffff8f73e2fa90 t NiNe
+)";
+  base::WriteAll(tmp.fd(), kContents, sizeof(kContents));
+  base::FlushFile(tmp.fd());
+
+  KernelSymbolMap kallsyms;
+  EXPECT_EQ(kallsyms.Lookup(0x42), "");
+
+  kallsyms.Parse(tmp.path().c_str());
+  EXPECT_EQ(kallsyms.num_syms(), 9u);
+
+  // Test first exact lookups.
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa10ULL), "one");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa20ULL), "two_");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa30LL), "_three");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa40ULL), "_fo_ur_");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa50ULL), "_five__");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa60ULL), "__si__x__");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa70ULL), "___se___v_e__n___");
+  EXPECT_EQ(
+      kallsyms.Lookup(0xffffff8f73e2fa80ULL),
+      "_eight_omg_this_name_is_so_loooooooooooooooooooooooooooooooooooong_"
+      "should_be_truncated_exactly_here_because_this_is_the_128_char");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa90ULL), "NiNe");
+
+  // Now check bound searches.
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa00ULL), "");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa11ULL), "one");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa19ULL), "one");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa71ULL), "___se___v_e__n___");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa91ULL), "NiNe");
+  EXPECT_EQ(kallsyms.Lookup(0xffffff8fffffffffULL), "NiNe");
+}
+
+TEST(KernelSymbolMapTest, GoldenTest) {
+  std::string fake_kallsyms;
+  fake_kallsyms.reserve(8 * 1024 * 1024);
+  static std::minstd_rand rng(0);
+  static const size_t kNameMax = 64;
+  std::map<uint64_t, std::string> symbols;
+  for (int rep = 0; rep < 1000; rep++) {
+    uint64_t addr = static_cast<uint64_t>(rng());
+    if (symbols.count(addr))
+      continue;
+    static const char kCharset[] = "_abcdef";
+    char sym_name[kNameMax + 1];  // Deliberately not initialized.
+    size_t sym_name_len = 1 + (rng() % kNameMax);
+    for (size_t i = 0; i < sym_name_len; i++)
+      sym_name[i] = kCharset[rng() % strlen(kCharset)];
+    sym_name[sym_name_len] = '\0';
+    char line[kNameMax + 40];
+    sprintf(line, "%" PRIx64 " t %s\n", addr, sym_name);
+    symbols[addr] = sym_name;
+    fake_kallsyms += line;
+  }
+  base::TempFile tmp = base::TempFile::Create();
+  base::WriteAll(tmp.fd(), fake_kallsyms.data(), fake_kallsyms.size());
+  base::FlushFile(tmp.fd());
+
+  KernelSymbolMap kallsyms;
+  kallsyms.Parse(tmp.path().c_str());
+  ASSERT_EQ(kallsyms.num_syms(), symbols.size());
+  for (const auto& kv : symbols) {
+    ASSERT_EQ(kallsyms.Lookup(kv.first), kv.second);
+  }
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/proto_translation_table.cc b/src/traced/probes/ftrace/proto_translation_table.cc
index 86c61f0..ceb0973 100644
--- a/src/traced/probes/ftrace/proto_translation_table.cc
+++ b/src/traced/probes/ftrace/proto_translation_table.cc
@@ -521,12 +521,12 @@
   group_to_events_[e->group].push_back(&events_.at(e->ftrace_event_id));
 
   return e;
-};
+}
 
 const char* ProtoTranslationTable::InternString(const std::string& str) {
   auto it_and_inserted = interned_strings_.insert(str);
   return it_and_inserted.first->c_str();
-};
+}
 
 uint16_t ProtoTranslationTable::CreateGenericEventField(
     const FtraceEvent::Field& ftrace_field,
diff --git a/src/traced/probes/ftrace/test/test_messages.proto b/src/traced/probes/ftrace/test/test_messages.proto
index 5118d62..1e6ece4 100644
--- a/src/traced/probes/ftrace/test/test_messages.proto
+++ b/src/traced/probes/ftrace/test/test_messages.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 message FakeFtraceEvent {
   optional uint32 common_field = 1;
diff --git a/src/traced/probes/metatrace/BUILD.gn b/src/traced/probes/metatrace/BUILD.gn
index 489b85a..c53789b 100644
--- a/src/traced/probes/metatrace/BUILD.gn
+++ b/src/traced/probes/metatrace/BUILD.gn
@@ -14,15 +14,17 @@
 
 source_set("metatrace") {
   public_deps = [
-    "../../../tracing",
+    "../../../tracing/core",
+    "../../../tracing/core:service",
   ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
+    "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/perfetto:zero",
     "../../../base",
-    "../../../tracing",
+    "../../../tracing/core",
   ]
   sources = [
     "metatrace_data_source.cc",
diff --git a/src/traced/probes/packages_list/BUILD.gn b/src/traced/probes/packages_list/BUILD.gn
index eaee21d..0f17596 100644
--- a/src/traced/probes/packages_list/BUILD.gn
+++ b/src/traced/probes/packages_list/BUILD.gn
@@ -15,15 +15,14 @@
 import("../../../../gn/test.gni")
 
 source_set("packages_list") {
-  public_deps = [
-    "../../../tracing",
-  ]
+  public_deps = [ "../../../tracing/core" ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
     "../../../../protos/perfetto/common:zero",
     "../../../../protos/perfetto/config/android:zero",
+    "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/android:zero",
     "../../../base",
   ]
@@ -42,9 +41,7 @@
     "../../../../protos/perfetto/trace/android:cpp",
     "../../../../protos/perfetto/trace/android:zero",
     "../../../../src/base:test_support",
-    "../../../../src/tracing:test_support",
+    "../../../../src/tracing/test:test_support",
   ]
-  sources = [
-    "packages_list_data_source_unittest.cc",
-  ]
+  sources = [ "packages_list_data_source_unittest.cc" ]
 }
diff --git a/src/traced/probes/power/BUILD.gn b/src/traced/probes/power/BUILD.gn
index d3af4a3..7b75c6a 100644
--- a/src/traced/probes/power/BUILD.gn
+++ b/src/traced/probes/power/BUILD.gn
@@ -13,14 +13,13 @@
 # limitations under the License.
 
 source_set("power") {
-  public_deps = [
-    "../../../tracing",
-  ]
+  public_deps = [ "../../../tracing/core" ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
     "../../../../protos/perfetto/config/power:zero",
+    "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/power:zero",
     "../../../android_internal:lazy_library_loader",
     "../../../base",
diff --git a/src/traced/probes/ps/BUILD.gn b/src/traced/probes/ps/BUILD.gn
index 85c24bc..d373ba3 100644
--- a/src/traced/probes/ps/BUILD.gn
+++ b/src/traced/probes/ps/BUILD.gn
@@ -15,14 +15,13 @@
 import("../../../../gn/test.gni")
 
 source_set("ps") {
-  public_deps = [
-    "../../../tracing",
-  ]
+  public_deps = [ "../../../tracing/core" ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
     "../../../../protos/perfetto/config/process_stats:zero",
+    "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/ps:zero",
     "../../../base",
   ]
@@ -41,9 +40,7 @@
     "../../../../protos/perfetto/config/process_stats:cpp",
     "../../../../protos/perfetto/trace/ps:cpp",
     "../../../../src/base:test_support",
-    "../../../../src/tracing:test_support",
+    "../../../../src/tracing/test:test_support",
   ]
-  sources = [
-    "process_stats_data_source_unittest.cc",
-  ]
+  sources = [ "process_stats_data_source_unittest.cc" ]
 }
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index fe73990..511aa23 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -241,6 +241,10 @@
 
   std::string cmdline = ReadProcPidFile(pid, "cmdline");
   if (!cmdline.empty()) {
+    if (cmdline.back() != '\0') {
+      // Some kernels can miss the NUL terminator due to a bug. b/147438623.
+      cmdline.push_back('\0');
+    }
     using base::StringSplitter;
     for (StringSplitter ss(&cmdline[0], cmdline.size(), '\0'); ss.Next();)
       proc->add_cmdline(ss.cur_token());
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index 04c683e..100ef62 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -23,7 +23,7 @@
 #include <unordered_map>
 #include <vector>
 
-#include "perfetto/ext/base/flat_set.h"
+#include "perfetto/base/flat_set.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
diff --git a/src/traced/probes/ps/process_stats_data_source_unittest.cc b/src/traced/probes/ps/process_stats_data_source_unittest.cc
index 532a27e..e3e4b9d 100644
--- a/src/traced/probes/ps/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/ps/process_stats_data_source_unittest.cc
@@ -91,6 +91,25 @@
   ASSERT_THAT(first_process.cmdline(), ElementsAreArray({"foo", "bar", "baz"}));
 }
 
+// Regression test for b/147438623.
+TEST_F(ProcessStatsDataSourceTest, NonNulTerminatedCmdline) {
+  auto data_source = GetProcessStatsDataSource(DataSourceConfig());
+  EXPECT_CALL(*data_source, ReadProcPidFile(42, "status"))
+      .WillOnce(Return(
+          "Name: foo\nTgid:\t42\nPid:   42\nPPid:  17\nUid:  43 44 45 56\n"));
+  EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline"))
+      .WillOnce(Return(std::string("surfaceflinger", 14)));
+
+  data_source->OnPids({42});
+
+  auto trace = writer_raw_->GetAllTracePackets();
+  ASSERT_EQ(trace.size(), 1u);
+  auto ps_tree = trace[0].process_tree();
+  ASSERT_EQ(ps_tree.processes_size(), 1);
+  auto first_process = ps_tree.processes()[0];
+  ASSERT_THAT(first_process.cmdline(), ElementsAreArray({"surfaceflinger"}));
+}
+
 TEST_F(ProcessStatsDataSourceTest, DontRescanCachedPIDsAndTIDs) {
   // assertion helpers
   auto expected_process = [](int pid) {
diff --git a/src/traced/probes/sys_stats/BUILD.gn b/src/traced/probes/sys_stats/BUILD.gn
index efea7a9..9c02da8 100644
--- a/src/traced/probes/sys_stats/BUILD.gn
+++ b/src/traced/probes/sys_stats/BUILD.gn
@@ -15,9 +15,7 @@
 import("../../../../gn/test.gni")
 
 source_set("sys_stats") {
-  public_deps = [
-    "../../../tracing",
-  ]
+  public_deps = [ "../../../tracing/core" ]
   deps = [
     "..:data_source",
     "../../../../gn:default_deps",
@@ -25,6 +23,7 @@
     "../../../../include/perfetto/ext/traced:sys_stats_counters",
     "../../../../protos/perfetto/common:zero",
     "../../../../protos/perfetto/config/sys_stats:zero",
+    "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/sys_stats:zero",
     "../../../base",
   ]
@@ -43,9 +42,7 @@
     "../../../../protos/perfetto/config/sys_stats:cpp",
     "../../../../protos/perfetto/trace/sys_stats:cpp",
     "../../../../src/base:test_support",
-    "../../../../src/tracing:test_support",
+    "../../../../src/tracing/test:test_support",
   ]
-  sources = [
-    "sys_stats_data_source_unittest.cc",
-  ]
+  sources = [ "sys_stats_data_source_unittest.cc" ]
 }
diff --git a/src/traced/service/BUILD.gn b/src/traced/service/BUILD.gn
index 340d3fa..2b7798b 100644
--- a/src/traced/service/BUILD.gn
+++ b/src/traced/service/BUILD.gn
@@ -27,23 +27,20 @@
     "../../../gn:default_deps",
     "../../../include/perfetto/ext/traced",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
   assert_no_deps = [ "//gn:protobuf_lite" ]
 }
 
 # Contains all the implementation but not the main() entry point. This target
 # is shared both by the executable and tests.
 source_set("service") {
-  public_deps = [
-    "../../../include/perfetto/ext/traced",
-  ]
+  public_deps = [ "../../../include/perfetto/ext/traced" ]
   deps = [
     "../../../gn:default_deps",
     "../../base",
-    "../../tracing",
-    "../../tracing:ipc",
+    "../../tracing/core",
+    "../../tracing/core:service",
+    "../../tracing/ipc/service",
   ]
   if (enable_perfetto_version_gen) {
     deps += [ "//gn/standalone:gen_git_revision" ]
@@ -63,9 +60,7 @@
     "../../../gn:gtest_and_gmock",
     "../../base",
     "../../base:test_support",
-    "../../tracing",
+    "../../tracing/core",
   ]
-  sources = [
-    "builtin_producer_unittest.cc",
-  ]
+  sources = [ "builtin_producer_unittest.cc" ]
 }
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index aa7c6f6..936eb2d 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -17,184 +17,51 @@
 import("../../gn/perfetto.gni")
 import("../../gn/test.gni")
 
-# Core tracing library, platform independent, no transport layer.
-source_set("tracing") {
+# Full version of the client API. Supports both the in-process backend and the
+# system backend (on posix systems and if enabled by the enable_perfetto_ipc).
+# It has a larger binary footprint due to the service code for the in-proecss
+# backend.
+group("client_api") {
   public_deps = [
-    ":common",
-    "../../include/perfetto/ext/tracing/core",
-    "../../protos/perfetto/trace:zero",
-    "../../protos/perfetto/trace/interned_data:zero",
-    "../../protos/perfetto/trace/track_event:zero",
-  ]
-  deps = [
+    ":client_api_base",
     "../../gn:default_deps",
-    "../../include/perfetto/tracing",
-    "../../protos/perfetto/common:zero",
-    "../../protos/perfetto/config:zero",
-    "../../protos/perfetto/trace/perfetto:zero",  # For MetatraceWriter.
-    "../base",
-    "../protozero",
   ]
-  sources = [
-    "core/id_allocator.cc",
-    "core/id_allocator.h",
-    "core/metatrace_writer.cc",
-    "core/metatrace_writer.h",
-    "core/null_trace_writer.cc",
-    "core/null_trace_writer.h",
-    "core/packet_stream_validator.cc",
-    "core/packet_stream_validator.h",
-    "core/patch_list.h",
-    "core/shared_memory_abi.cc",
-    "core/shared_memory_arbiter_impl.cc",
-    "core/shared_memory_arbiter_impl.h",
-    "core/startup_trace_writer.cc",
-    "core/startup_trace_writer_registry.cc",
-    "core/trace_buffer.cc",
-    "core/trace_buffer.h",
-    "core/trace_packet.cc",
-    "core/trace_writer_impl.cc",
-    "core/trace_writer_impl.h",
-    "core/tracing_service_impl.cc",
-    "core/tracing_service_impl.h",
-    "core/virtual_destructors.cc",
-  ]
-}
-
-perfetto_unittest_source_set("unittests") {
-  testonly = true
-  deps = [
-    ":test_support",
-    ":tracing",
-    "../../gn:default_deps",
-    "../../gn:gtest_and_gmock",
-    "../../protos/perfetto/trace:cpp",
-    "../../protos/perfetto/trace:zero",
-    "../../protos/perfetto/trace/ftrace:cpp",
-    "../base",
-    "../base:test_support",
-  ]
-  sources = [
-    "core/id_allocator_unittest.cc",
-    "core/null_trace_writer_unittest.cc",
-    "core/packet_stream_validator_unittest.cc",
-    "core/patch_list_unittest.cc",
-    "core/shared_memory_abi_unittest.cc",
-    "core/trace_buffer_unittest.cc",
-    "core/trace_packet_unittest.cc",
-    "test/aligned_buffer_test.cc",
-    "test/aligned_buffer_test.h",
-    "test/fake_packet.cc",
-    "test/fake_packet.h",
-    "test/test_shared_memory.cc",
-    "test/test_shared_memory.h",
-  ]
-
+  deps = [ ":in_process_backend" ]
   if (enable_perfetto_ipc) {
-    deps += [ ":ipc" ]
-    sources += [
-      "ipc/posix_shared_memory_unittest.cc",
-      "test/tracing_integration_test.cc",
-    ]
-  }
-
-  # These tests rely on test_task_runner.h which
-  # has no Windows implementation.
-  if (!is_win) {
-    sources += [
-      "core/shared_memory_arbiter_impl_unittest.cc",
-      "core/startup_trace_writer_unittest.cc",
-      "core/trace_writer_impl_unittest.cc",
-      "core/tracing_service_impl_unittest.cc",
-      "test/fake_producer_endpoint.h",
-      "test/mock_consumer.cc",
-      "test/mock_consumer.h",
-      "test/mock_producer.cc",
-      "test/mock_producer.h",
-    ]
+    deps += [ ":system_process_backend" ]
+  } else {
+    deps += [ ":system_process_backend_fake" ]
   }
 }
 
-source_set("test_support") {
-  testonly = true
-  public_deps = [
-    "../../include/perfetto/ext/tracing/core",
-    "../../protos/perfetto/trace:cpp",
-    "../../protos/perfetto/trace:zero",
-    "../protozero",
-  ]
-  sources = [
-    "core/trace_writer_for_testing.cc",
-    "core/trace_writer_for_testing.h",
-  ]
-}
-
-if (perfetto_build_standalone || perfetto_build_with_android) {
-  executable("consumer_api_test") {
-    testonly = true
-    deps = [
-      ":consumer_api_deprecated",
-      "../../gn:default_deps",
-      "../../include/perfetto/public",
-      "../../protos/perfetto/config:cpp",
-      "../../protos/perfetto/config/ftrace:cpp",
-      "../../protos/perfetto/trace:cpp",
-      "../../protos/perfetto/trace/ftrace:cpp",
-      "../base",
-    ]
-    sources = [
-      "api_impl/consumer_api_test.cc",
-    ]
-  }
-
-  # Imlementation of the public-facing consumer API in libperfetto.so (only for
-  # Android builds).
-  # TODO(primiano): remove this. This is a legacy and deprecated API. The only
-  # uses should be moved to the perfetto Client API.
-  source_set("consumer_api_deprecated") {
-    deps = [
-      ":ipc",
-      ":tracing",
-      "../../gn:default_deps",
-      "../../include/perfetto/public",
-      "../../protos/perfetto/config:cpp",
-      "../base",
-    ]
-    sources = [
-      "api_impl/consumer_api.cc",
-    ]
-  }
-}
-
+# Slim version of the client API. Works only with the system backend (traced
+# connection over a UNIX socket). Has a lighter binary size impact.
 if (enable_perfetto_ipc) {
-  # Posix specialization of the tracing library for Linux / Android / Mac.
-  # Provides an IPC transport over a UNIX domain socket.
-  source_set("ipc") {
+  source_set("client_api_system_backend_only") {
     public_deps = [
-      "../../include/perfetto/ext/tracing/core",
-      "../../include/perfetto/ext/tracing/ipc",
-    ]
-    sources = [
-      "ipc/consumer/consumer_ipc_client_impl.cc",
-      "ipc/consumer/consumer_ipc_client_impl.h",
-      "ipc/default_socket.cc",
-      "ipc/posix_shared_memory.cc",
-      "ipc/posix_shared_memory.h",
-      "ipc/producer/producer_ipc_client_impl.cc",
-      "ipc/producer/producer_ipc_client_impl.h",
-      "ipc/service/consumer_ipc_service.cc",
-      "ipc/service/consumer_ipc_service.h",
-      "ipc/service/producer_ipc_service.cc",
-      "ipc/service/producer_ipc_service.h",
-      "ipc/service/service_ipc_host_impl.cc",
-      "ipc/service/service_ipc_host_impl.h",
+      ":client_api_base",
+      "../../gn:default_deps",
     ]
     deps = [
-      ":tracing",
+      ":in_process_backend_fake",
+      ":system_process_backend",
+    ]
+  }
+}
+
+# This target checks only that the "fake" backends compile. This is to detect
+# early if we break them with refactorings, without waiting for TreeHugger or
+# rolls into chromium.
+if (perfetto_build_standalone) {
+  shared_library("client_api_no_backends_compile_test") {
+    public_deps = [
+      ":client_api_base",
       "../../gn:default_deps",
-      "../../protos/perfetto/ipc",
-      "../base",
-      "../ipc",
+    ]
+    deps = [
+      ":in_process_backend_fake",
+      ":platform_fake",
+      ":system_process_backend_fake",
     ]
   }
 }
@@ -207,12 +74,20 @@
       "../../include/perfetto/tracing",
       "../base",
     ]
-    sources = [
-      "platform_posix.cc",
-    ]
+    sources = [ "platform_posix.cc" ]
   }
 }
 
+# Fake platform that allows buiding the client lib on all OSes. You can only use
+# those parts of the client lib that do not use the platform.
+source_set("platform_fake") {
+  deps = [
+    "../../gn:default_deps",
+    "../../include/perfetto/tracing",
+  ]
+  sources = [ "platform_fake.cc" ]
+}
+
 # Code that both public headers and other non-public sources (e.g.
 # src/tracing/core) need to depend on. It cannot be in the root :tracing target
 # otherwise there would be a cyclic dependency because public itself needs to
@@ -222,18 +97,16 @@
     "../../gn:default_deps",
     "../../include/perfetto/tracing",
   ]
-  sources = [
-    "trace_writer_base.cc",
-  ]
+  sources = [ "trace_writer_base.cc" ]
 }
 
-source_set("client_api") {
+# Base target for the client API. On its own doesn't provide any backend.
+source_set("client_api_base") {
   deps = [
-    ":common",
     "../../include/perfetto/tracing/core",
     "../../protos/perfetto/config:cpp",
     "../base",
-    "../tracing",
+    "core",
   ]
   public_deps = [
     "../../gn:default_deps",
@@ -243,78 +116,70 @@
     "data_source.cc",
     "debug_annotation.cc",
     "event_context.cc",
-    "internal/in_process_tracing_backend.cc",
     "internal/in_process_tracing_backend.h",
+    "internal/system_tracing_backend.h",
     "internal/tracing_muxer_impl.cc",
     "internal/tracing_muxer_impl.h",
     "internal/track_event_internal.cc",
     "platform.cc",
     "tracing.cc",
+    "track.cc",
     "track_event_category_registry.cc",
     "virtual_destructors.cc",
   ]
-
-  if (enable_perfetto_ipc) {
-    deps += [ ":ipc" ]
-    sources += [
-      "internal/system_tracing_backend.cc",
-      "internal/system_tracing_backend.h",
-    ]
-  }
 }
 
-if (enable_perfetto_integration_tests) {
-  source_set("client_api_integrationtests") {
-    testonly = true
+# System backend: connects to an external "traced" instance via a UNIX socket.
+# Requires the IPC layer and is supported only on posix systems.
+if (enable_perfetto_ipc) {
+  source_set("system_process_backend") {
+    public_deps = [ "../../include/perfetto/tracing" ]
     deps = [
-      ":client_api",
-      ":platform_posix",
-      "../../:libperfetto_client_experimental",
+      ":client_api_base",
       "../../gn:default_deps",
-      "../../gn:gtest_and_gmock",
       "../../include/perfetto/tracing/core",
-      "../../protos/perfetto/trace:cpp",
-      "../../protos/perfetto/trace:zero",
-      "../../protos/perfetto/trace/interned_data:cpp",
-      "../../protos/perfetto/trace/interned_data:zero",
-      "../../protos/perfetto/trace/profiling:cpp",
-      "../../protos/perfetto/trace/track_event:cpp",
       "../base",
-      "test:api_test_support",
+      "ipc/consumer",
+      "ipc/producer",
+      "ipc/service",
     ]
-    sources = [
-      "api_integrationtest.cc",
-      "test/tracing_module.cc",
-      "test/tracing_module.h",
-      "test/tracing_module2.cc",
-      "test/tracing_module_categories.h",
-    ]
+    sources = [ "internal/system_tracing_backend.cc" ]
   }
 }
 
-if (enable_perfetto_benchmarks) {
-  source_set("benchmarks") {
-    testonly = true
-    deps = [
-      ":tracing",
-      "../../../../gn:benchmark",
-      "../../../../gn:default_deps",
-      "../../protos/perfetto/trace:zero",
-      "../../protos/perfetto/trace/ftrace:zero",
-      "../protozero",
-    ]
-    sources = [
-      "core/packet_stream_validator_benchmark.cc",
-    ]
-  }
-}
-
-perfetto_fuzzer_test("packet_stream_validator_fuzzer") {
-  sources = [
-    "core/packet_stream_validator_fuzzer.cc",
-  ]
+# System backend fallback: it prints an error message and returns nullptr.
+source_set("system_process_backend_fake") {
+  public_deps = [ "../../include/perfetto/tracing" ]
   deps = [
-    ":tracing",
-    "../../../../gn:default_deps",
+    ":client_api_base",
+    "../../gn:default_deps",
+    "../base",
   ]
+  sources = [ "internal/system_tracing_backend_fake.cc" ]
+}
+
+# In-process backend: starts the tracing service in-process on a dedicated
+# thread. It depends only on having a valid "platform" target. It has a larger
+# binary size cost because links in all the service code.
+source_set("in_process_backend") {
+  public_deps = [ "../../include/perfetto/tracing" ]
+  deps = [
+    ":client_api_base",
+    "../../gn:default_deps",
+    "../../include/perfetto/tracing/core",
+    "../base",
+    "core:service",
+  ]
+  sources = [ "internal/in_process_tracing_backend.cc" ]
+}
+
+# In-process backend fallback: it prints an error messaage and returns nullptr.
+source_set("in_process_backend_fake") {
+  public_deps = [ "../../include/perfetto/tracing" ]
+  deps = [
+    ":client_api_base",
+    "../../gn:default_deps",
+    "../base",
+  ]
+  sources = [ "internal/in_process_tracing_backend_fake.cc" ]
 }
diff --git a/src/tracing/consumer_api_deprecated/BUILD.gn b/src/tracing/consumer_api_deprecated/BUILD.gn
new file mode 100644
index 0000000..1889e41
--- /dev/null
+++ b/src/tracing/consumer_api_deprecated/BUILD.gn
@@ -0,0 +1,50 @@
+# Copyright (C) 2020 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.
+
+# Imlementation of the public-facing consumer API in libperfetto.so (only for
+# Android builds).
+# TODO(primiano): remove this. This is a legacy and deprecated API. The only
+# user is iorap which should be moved to the perfetto Client API.
+
+import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
+
+assert(perfetto_build_standalone || perfetto_build_with_android)
+
+source_set("consumer_api_deprecated") {
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/public",
+    "../../../protos/perfetto/config:cpp",
+    "../../base",
+    "../core",
+    "../ipc/consumer",
+  ]
+  sources = [ "consumer_api_deprecated.cc" ]
+}
+
+executable("consumer_api_test") {
+  testonly = true
+  deps = [
+    ":consumer_api_deprecated",
+    "../../../gn:default_deps",
+    "../../../include/perfetto/public",
+    "../../../protos/perfetto/config:cpp",
+    "../../../protos/perfetto/config/ftrace:cpp",
+    "../../../protos/perfetto/trace:cpp",
+    "../../../protos/perfetto/trace/ftrace:cpp",
+    "../../base",
+  ]
+  sources = [ "consumer_api_deprecated_test.cc" ]
+}
diff --git a/src/tracing/api_impl/consumer_api.cc b/src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc
similarity index 100%
rename from src/tracing/api_impl/consumer_api.cc
rename to src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc
diff --git a/src/tracing/api_impl/consumer_api_test.cc b/src/tracing/consumer_api_deprecated/consumer_api_deprecated_test.cc
similarity index 100%
rename from src/tracing/api_impl/consumer_api_test.cc
rename to src/tracing/consumer_api_deprecated/consumer_api_deprecated_test.cc
diff --git a/src/tracing/core/BUILD.gn b/src/tracing/core/BUILD.gn
new file mode 100644
index 0000000..df17b7d
--- /dev/null
+++ b/src/tracing/core/BUILD.gn
@@ -0,0 +1,150 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../gn/fuzzer.gni")
+import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
+
+# Core tracing library, platform independent, no IPC layer, no service.
+source_set("core") {
+  public_deps = [
+    "../../../include/perfetto/ext/tracing/core",
+    "../../protozero",
+  ]
+  deps = [
+    "..:common",
+    "../../../gn:default_deps",
+    "../../../include/perfetto/tracing",
+    "../../../protos/perfetto/trace:zero",
+    "../../base",
+  ]
+  sources = [
+    "id_allocator.cc",
+    "id_allocator.h",
+    "null_trace_writer.cc",
+    "null_trace_writer.h",
+    "patch_list.h",
+    "shared_memory_abi.cc",
+    "shared_memory_arbiter_impl.cc",
+    "shared_memory_arbiter_impl.h",
+    "startup_trace_writer.cc",
+    "startup_trace_writer_registry.cc",
+    "trace_packet.cc",
+    "trace_writer_impl.cc",
+    "trace_writer_impl.h",
+    "virtual_destructors.cc",
+  ]
+}
+
+source_set("service") {
+  public_deps = [
+    "..:common",
+    "../../../include/perfetto/ext/base",
+    "../../../include/perfetto/ext/tracing/core",
+  ]
+  deps = [
+    ":core",
+    "../../../gn:default_deps",
+    "../../../include/perfetto/tracing",
+    "../../../protos/perfetto/common:zero",
+    "../../../protos/perfetto/config:zero",
+    "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/perfetto:zero",  # For MetatraceWriter.
+    "../../base",
+  ]
+  sources = [
+    "metatrace_writer.cc",
+    "metatrace_writer.h",
+    "packet_stream_validator.cc",
+    "packet_stream_validator.h",
+    "trace_buffer.cc",
+    "trace_buffer.h",
+    "tracing_service_impl.cc",
+    "tracing_service_impl.h",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":core",
+    ":service",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../protos/perfetto/trace:cpp",
+    "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/ftrace:cpp",
+    "../../base",
+    "../../base:test_support",
+    "../test:test_support",
+  ]
+  sources = [
+    "id_allocator_unittest.cc",
+    "null_trace_writer_unittest.cc",
+    "packet_stream_validator_unittest.cc",
+    "patch_list_unittest.cc",
+    "shared_memory_abi_unittest.cc",
+    "trace_buffer_unittest.cc",
+    "trace_packet_unittest.cc",
+  ]
+
+  # These tests rely on test_task_runner.h which
+  # has no Windows implementation.
+  if (!is_win) {
+    sources += [
+      "shared_memory_arbiter_impl_unittest.cc",
+      "startup_trace_writer_unittest.cc",
+      "trace_writer_impl_unittest.cc",
+      "tracing_service_impl_unittest.cc",
+    ]
+  }
+}
+
+perfetto_unittest_source_set("test_support") {
+  testonly = true
+  public_deps = [
+    "../../../include/perfetto/ext/tracing/core",
+    "../../../protos/perfetto/trace:cpp",
+    "../../../protos/perfetto/trace:zero",
+    "../../protozero",
+  ]
+  sources = [
+    "trace_writer_for_testing.cc",
+    "trace_writer_for_testing.h",
+  ]
+}
+
+if (enable_perfetto_benchmarks) {
+  source_set("benchmarks") {
+    testonly = true
+    deps = [
+      ":core",
+      ":service",
+      "../../../../../gn:benchmark",
+      "../../../../../gn:default_deps",
+      "../../../protos/perfetto/trace:zero",
+      "../../../protos/perfetto/trace/ftrace:zero",
+      "../../protozero",
+    ]
+    sources = [ "packet_stream_validator_benchmark.cc" ]
+  }
+}
+
+perfetto_fuzzer_test("packet_stream_validator_fuzzer") {
+  sources = [ "packet_stream_validator_fuzzer.cc" ]
+  deps = [
+    ":service",
+    "../../../../../gn:default_deps",
+  ]
+}
diff --git a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
index 91bd5db..036a8a3 100644
--- a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
@@ -50,7 +50,7 @@
       BufferExhaustedPolicy) override {
     return nullptr;
   }
-  SharedMemoryArbiter* GetInProcessShmemArbiter() override { return nullptr; }
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
 
   MOCK_METHOD2(CommitData, void(const CommitDataRequest&, CommitDataCallback));
   MOCK_METHOD2(RegisterTraceWriter, void(uint32_t, uint32_t));
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index f40606f..24e8a0b 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -25,7 +25,8 @@
 #include <regex>
 #include <unordered_set>
 
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
 #include <sys/uio.h>
 #include <sys/utsname.h>
 #include <unistd.h>
@@ -76,6 +77,7 @@
 constexpr base::TimeMillis kSnapshotsInterval(10 * 1000);
 constexpr int kDefaultWriteIntoFilePeriodMs = 5000;
 constexpr int kMaxConcurrentTracingSessions = 15;
+constexpr int kMaxConcurrentTracingSessionsPerUid = 5;
 constexpr int64_t kMinSecondsBetweenTracesGuardrail = 5 * 60;
 
 constexpr uint32_t kMillisPerHour = 3600000;
@@ -85,7 +87,7 @@
 constexpr uint32_t kGuardrailsMaxTracingBufferSizeKb = 128 * 1024;
 constexpr uint32_t kGuardrailsMaxTracingDurationMillis = 24 * kMillisPerHour;
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
 struct iovec {
   void* iov_base;  // Address
   size_t iov_len;  // Block size
@@ -114,7 +116,8 @@
 uid_t geteuid() {
   return 0;
 }
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
 
 // Partially encodes a CommitDataRequest in an int32 for the purposes of
 // metatracing. Note that it encodes only the bottom 10 bits of the producer id
@@ -466,6 +469,17 @@
     }
   }
 
+  const long sessions_for_uid = std::count_if(
+      tracing_sessions_.begin(), tracing_sessions_.end(),
+      [consumer](const decltype(tracing_sessions_)::value_type& s) {
+        return s.second.consumer_uid == consumer->uid_;
+      });
+  if (sessions_for_uid >= kMaxConcurrentTracingSessionsPerUid) {
+    PERFETTO_ELOG("Too many concurrent tracing sesions (%ld) for uid %d",
+                  sessions_for_uid, static_cast<int>(consumer->uid_));
+    return false;
+  }
+
   // TODO(primiano): This is a workaround to prevent that a producer gets stuck
   // in a state where it stalls by design by having more TraceWriterImpl
   // instances than free pages in the buffer. This is really a bug in
@@ -2223,7 +2237,8 @@
   auto* clock_snapshot = packet->set_clock_snapshot();
 
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&    \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   struct {
     clockid_t id;
     protos::pbzero::ClockSnapshot::Clock::BuiltinClocks type;
@@ -2266,8 +2281,9 @@
     c->set_timestamp(
         static_cast<uint64_t>(base::FromPosixTimespec(clock.ts).count()));
   }
-#else   // !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) &&
-        // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#else  // !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) &&
+       // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&
+       // !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   auto wall_time_ns = static_cast<uint64_t>(base::GetWallTimeNs().count());
   if (set_root_timestamp)
     root_timestamp_ns = wall_time_ns;
@@ -2347,7 +2363,8 @@
   protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
   auto* info = packet->set_system_info();
   base::ignore_result(info);  // For PERFETTO_OS_WIN.
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   struct utsname uname_info;
   if (uname(&uname_info) == 0) {
     auto* utsname_info = info->set_utsname();
@@ -2803,7 +2820,7 @@
 }
 
 SharedMemoryArbiter*
-TracingServiceImpl::ProducerEndpointImpl::GetInProcessShmemArbiter() {
+TracingServiceImpl::ProducerEndpointImpl::MaybeSharedMemoryArbiter() {
   if (!inproc_shmem_arbiter_) {
     PERFETTO_FATAL(
         "The in-process SharedMemoryArbiter can only be used when "
@@ -2820,14 +2837,16 @@
 TracingServiceImpl::ProducerEndpointImpl::CreateTraceWriter(
     BufferID buf_id,
     BufferExhaustedPolicy buffer_exhausted_policy) {
-  return GetInProcessShmemArbiter()->CreateTraceWriter(buf_id,
+  PERFETTO_DCHECK(MaybeSharedMemoryArbiter());
+  return MaybeSharedMemoryArbiter()->CreateTraceWriter(buf_id,
                                                        buffer_exhausted_policy);
 }
 
 void TracingServiceImpl::ProducerEndpointImpl::NotifyFlushComplete(
     FlushRequestID id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  return GetInProcessShmemArbiter()->NotifyFlushComplete(id);
+  PERFETTO_DCHECK(MaybeSharedMemoryArbiter());
+  return MaybeSharedMemoryArbiter()->NotifyFlushComplete(id);
 }
 
 void TracingServiceImpl::ProducerEndpointImpl::OnTracingSetup() {
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 61c6ef6..eb53207 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -90,7 +90,7 @@
     std::unique_ptr<TraceWriter> CreateTraceWriter(
         BufferID,
         BufferExhaustedPolicy) override;
-    SharedMemoryArbiter* GetInProcessShmemArbiter() override;
+    SharedMemoryArbiter* MaybeSharedMemoryArbiter() override;
     void NotifyFlushComplete(FlushRequestID) override;
     void NotifyDataSourceStarted(DataSourceInstanceID) override;
     void NotifyDataSourceStopped(DataSourceInstanceID) override;
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index 2643cc7..bedb3f1 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -2931,4 +2931,40 @@
   EXPECT_EQ(svc_state.data_sources().at(1).ds_descriptor().name(), "p2_ds");
 }
 
+TEST_F(TracingServiceImplTest, LimitSessionsPerUid) {
+  std::vector<std::unique_ptr<MockConsumer>> consumers;
+
+  auto start_new_session = [&](uid_t uid) -> MockConsumer* {
+    TraceConfig trace_config;
+    trace_config.add_buffers()->set_size_kb(128);
+    trace_config.set_duration_ms(0);  // Unlimited.
+    consumers.emplace_back(CreateMockConsumer());
+    consumers.back()->Connect(svc.get(), uid);
+    consumers.back()->EnableTracing(trace_config);
+    return &*consumers.back();
+  };
+
+  const int kMaxConcurrentTracingSessionsPerUid = 5;
+  const int kUids = 2;
+
+  // Create a bunch of legit sessions (2 uids * 5 sessions).
+  for (int i = 0; i < kMaxConcurrentTracingSessionsPerUid * kUids; i++) {
+    start_new_session(/*uid=*/i % kUids);
+  }
+
+  // Any other session now should fail for the two uids.
+  for (int i = 0; i <= kUids; i++) {
+    auto* consumer = start_new_session(/*uid=*/i % kUids);
+    auto on_fail = task_runner.CreateCheckpoint("uid_" + std::to_string(i));
+    EXPECT_CALL(*consumer, OnTracingDisabled()).WillOnce(Invoke(on_fail));
+  }
+
+  // Wait for failure (only after both attempts).
+  for (int i = 0; i <= kUids; i++) {
+    task_runner.RunUntilCheckpoint("uid_" + std::to_string(i));
+  }
+
+  // The destruction of |consumers| will tear down and stop the good sessions.
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/event_context.cc b/src/tracing/event_context.cc
index 2126e2c..a4f1ade 100644
--- a/src/tracing/event_context.cc
+++ b/src/tracing/event_context.cc
@@ -29,6 +29,9 @@
       incremental_state_(incremental_state) {}
 
 EventContext::~EventContext() {
+  if (!trace_packet_)
+    return;
+
   // When the track event is finalized (i.e., the context is destroyed), we
   // should flush any newly seen interned data to the trace. The data has
   // earlier been written to a heap allocated protobuf message
diff --git a/src/tracing/internal/in_process_tracing_backend_fake.cc b/src/tracing/internal/in_process_tracing_backend_fake.cc
new file mode 100644
index 0000000..7ffafa8
--- /dev/null
+++ b/src/tracing/internal/in_process_tracing_backend_fake.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 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/tracing/internal/in_process_tracing_backend.h"
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace internal {
+
+// static
+InProcessTracingBackend* InProcessTracingBackend::GetInstance() {
+  PERFETTO_ELOG(
+      "In-process tracing backend not supported by the current build "
+      "configuration");
+  return nullptr;
+}
+
+}  // namespace internal
+}  // namespace perfetto
diff --git a/test/task_runner_thread_delegates.cc b/src/tracing/internal/system_tracing_backend_fake.cc
similarity index 60%
copy from test/task_runner_thread_delegates.cc
copy to src/tracing/internal/system_tracing_backend_fake.cc
index 291482f..6a940ae 100644
--- a/test/task_runner_thread_delegates.cc
+++ b/src/tracing/internal/system_tracing_backend_fake.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,12 +14,20 @@
  * limitations under the License.
  */
 
-#include "test/task_runner_thread_delegates.h"
+#include "src/tracing/internal/system_tracing_backend.h"
+
+#include "perfetto/base/logging.h"
 
 namespace perfetto {
+namespace internal {
 
-ServiceDelegate::~ServiceDelegate() = default;
-ProbesProducerDelegate::~ProbesProducerDelegate() = default;
-FakeProducerDelegate::~FakeProducerDelegate() = default;
+// static
+SystemTracingBackend* SystemTracingBackend::GetInstance() {
+  PERFETTO_ELOG(
+      "System tracing backend not supported by the current build "
+      "configuration");
+  return nullptr;
+}
 
+}  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index a185f25..e63bc11 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -421,6 +421,12 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);  // Rebind the thread checker.
 
   auto add_backend = [this, &args](TracingBackend* backend, BackendType type) {
+    if (!backend) {
+      // We skip the log in release builds because the *_backend_fake.cc code
+      // has already an ELOG before returning a nullptr.
+      PERFETTO_DLOG("Backend creation failed, type %d", static_cast<int>(type));
+      return;
+    }
     TracingBackendId backend_id = backends_.size();
     backends_.emplace_back();
     RegisteredBackend& rb = backends_.back();
@@ -437,13 +443,12 @@
     rb.producer->Initialize(rb.backend->ConnectProducer(conn_args));
   };
 
-  if (args.backends & kSystemBackend) {
-#if (PERFETTO_BUILDFLAG(PERFETTO_IPC))
+  // Both the system and the in-process backends can be disabled at build-time
+  // and replaced with the _fake.cc versions. The "fake" versions will just
+  // ELOG() and return nullptr.
+
+  if (args.backends & kSystemBackend)
     add_backend(SystemTracingBackend::GetInstance(), kSystemBackend);
-#else
-    PERFETTO_ELOG("System backend not supporteed in the current configuration");
-#endif
-  }
 
   if (args.backends & kInProcessBackend)
     add_backend(InProcessTracingBackend::GetInstance(), kInProcessBackend);
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 71afc7b..4c0c86b 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -16,9 +16,9 @@
 
 #include "perfetto/tracing/internal/track_event_internal.h"
 
+#include "perfetto/base/proc_utils.h"
+#include "perfetto/base/thread_utils.h"
 #include "perfetto/base/time.h"
-#include "perfetto/ext/base/proc_utils.h"
-#include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/track_event.h"
 #include "perfetto/tracing/track_event_category_registry.h"
@@ -28,8 +28,7 @@
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
-#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
-#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
 
 namespace perfetto {
 namespace internal {
@@ -38,7 +37,7 @@
 
 namespace {
 
-std::atomic<perfetto::base::PlatformThreadID> g_main_thread;
+std::atomic<perfetto::base::PlatformThreadId> g_main_thread;
 
 struct InternedEventCategory
     : public TrackEventInternedDataIndex<
@@ -95,49 +94,6 @@
 #endif
 }
 
-uint64_t GetTimeNs() {
-  if (GetClockType() == protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
-    return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
-  PERFETTO_DCHECK(GetClockType() ==
-                  protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
-  return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
-}
-
-protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
-    TraceWriterBase* trace_writer,
-    uint64_t timestamp) {
-  auto packet = trace_writer->NewTracePacket();
-  packet->set_timestamp(timestamp);
-  // TODO(skyostil): Stop emitting this for every event once the trace processor
-  // understands trace packet defaults.
-  if (GetClockType() != protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
-    packet->set_timestamp_clock_id(GetClockType());
-  return packet;
-}
-
-// static
-void WriteSequenceDescriptors(TraceWriterBase* trace_writer,
-                              uint64_t timestamp) {
-  if (perfetto::base::GetThreadId() == g_main_thread) {
-    auto packet = NewTracePacket(trace_writer, timestamp);
-    packet->set_incremental_state_cleared(true);
-    auto defaults = packet->set_trace_packet_defaults();
-    defaults->set_timestamp_clock_id(GetClockType());
-    auto pd = packet->set_process_descriptor();
-    pd->set_pid(static_cast<int32_t>(base::GetProcessId()));
-    // TODO(skyostil): Record command line.
-  }
-  {
-    auto packet = NewTracePacket(trace_writer, timestamp);
-    packet->set_incremental_state_cleared(true);
-    auto defaults = packet->set_trace_packet_defaults();
-    defaults->set_timestamp_clock_id(GetClockType());
-    auto td = packet->set_thread_descriptor();
-    td->set_pid(static_cast<int32_t>(base::GetProcessId()));
-    td->set_tid(static_cast<int32_t>(perfetto::base::GetThreadId()));
-  }
-}
-
 }  // namespace
 
 // static
@@ -177,6 +133,57 @@
 }
 
 // static
+uint64_t TrackEventInternal::GetTimeNs() {
+  if (GetClockType() == protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
+    return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
+  PERFETTO_DCHECK(GetClockType() ==
+                  protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
+  return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+}
+
+// static
+void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
+                                               uint64_t timestamp) {
+  auto default_track = ThreadTrack::Current();
+  {
+    // Mark any incremental state before this point invalid. Also set up
+    // defaults so that we don't need to repeat constant data for each packet.
+    auto packet = NewTracePacket(
+        trace_writer, timestamp,
+        protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
+    auto defaults = packet->set_trace_packet_defaults();
+    defaults->set_timestamp_clock_id(GetClockType());
+
+    // Establish the default track for this event sequence.
+    auto track_defaults = defaults->set_track_event_defaults();
+    track_defaults->set_track_uuid(default_track.uuid);
+  }
+
+  // Every thread should write a descriptor for its default track, because most
+  // trace points won't explicitly reference it.
+  WriteTrackDescriptor(default_track, trace_writer);
+
+  // Additionally the main thread should dump the process descriptor.
+  if (perfetto::base::GetThreadId() == g_main_thread)
+    WriteTrackDescriptor(ProcessTrack::Current(), trace_writer);
+}
+
+// static
+protozero::MessageHandle<protos::pbzero::TracePacket>
+TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
+                                   uint64_t timestamp,
+                                   uint32_t seq_flags) {
+  auto packet = trace_writer->NewTracePacket();
+  packet->set_timestamp(timestamp);
+  // TODO(skyostil): Stop emitting this for every event once the trace processor
+  // understands trace packet defaults.
+  if (GetClockType() != protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
+    packet->set_timestamp_clock_id(GetClockType());
+  packet->set_sequence_flags(seq_flags);
+  return packet;
+}
+
+// static
 EventContext TrackEventInternal::WriteEvent(
     TraceWriterBase* trace_writer,
     TrackEventIncrementalState* incr_state,
@@ -189,19 +196,22 @@
 
   if (incr_state->was_cleared) {
     incr_state->was_cleared = false;
-    WriteSequenceDescriptors(trace_writer, timestamp);
+    ResetIncrementalState(trace_writer, timestamp);
   }
   auto packet = NewTracePacket(trace_writer, timestamp);
+  EventContext ctx(std::move(packet), incr_state);
+
+  auto track_event = ctx.event();
+  if (type != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
+    track_event->set_type(type);
 
   // We assume that |category| and |name| point to strings with static lifetime.
   // This means we can use their addresses as interning keys.
-  EventContext ctx(std::move(packet), incr_state);
-  size_t category_iid = InternedEventCategory::Get(&ctx, category);
-
-  auto track_event = ctx.event();
-  track_event->set_type(type);
-  // TODO(skyostil): Handle multiple categories.
-  track_event->add_category_iids(category_iid);
+  if (type != protos::pbzero::TrackEvent::TYPE_SLICE_END) {
+    // TODO(skyostil): Handle multiple categories.
+    size_t category_iid = InternedEventCategory::Get(&ctx, category);
+    track_event->add_category_iids(category_iid);
+  }
   if (name) {
     size_t name_iid = InternedEventName::Get(&ctx, name);
     track_event->set_name_iid(name_iid);
diff --git a/src/tracing/ipc/BUILD.gn b/src/tracing/ipc/BUILD.gn
new file mode 100644
index 0000000..0f2bcb6
--- /dev/null
+++ b/src/tracing/ipc/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
+
+assert(enable_perfetto_ipc)
+
+# The lack of a default "ipc" target is deliberate. Clients need to explicitly
+# depend on ipc/{producer, consumer, service}. This is to avoid binary bloat
+# by always linking everything.
+
+source_set("common") {
+  public_deps = [
+    "../../../include/perfetto/ext/tracing/core",
+    "../../../include/perfetto/ext/tracing/ipc",
+  ]
+  sources = [
+    "default_socket.cc",
+    "posix_shared_memory.cc",
+    "posix_shared_memory.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/ipc",
+    "../../base",
+    "../core",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":common",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../include/perfetto/ext/ipc",
+    "../../base",
+    "../../base:test_support",
+  ]
+  sources = [ "posix_shared_memory_unittest.cc" ]
+}
diff --git a/src/tracing/ipc/consumer/BUILD.gn b/src/tracing/ipc/consumer/BUILD.gn
new file mode 100644
index 0000000..0a2dcc2
--- /dev/null
+++ b/src/tracing/ipc/consumer/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../gn/perfetto.gni")
+
+assert(enable_perfetto_ipc)
+
+# Posix specialization of the tracing library for Linux / Android / Mac.
+# Provides an IPC transport over a UNIX socket for the consumer interface.
+source_set("consumer") {
+  public_deps = [
+    "../../../../include/perfetto/ext/tracing/core",
+    "../../../../include/perfetto/ext/tracing/ipc",
+    "../../../../protos/perfetto/ipc",
+  ]
+  sources = [
+    "consumer_ipc_client_impl.cc",
+    "consumer_ipc_client_impl.h",
+  ]
+  deps = [
+    "..:common",
+    "../../../../gn:default_deps",
+    "../../../base",
+    "../../../ipc:client",
+  ]
+}
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
index e65f1fc..66c7943 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
@@ -29,6 +29,7 @@
 #include "perfetto/ext/tracing/core/tracing_service.h"
 #include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
 #include "perfetto/tracing/core/forward_decls.h"
+
 #include "protos/perfetto/ipc/consumer_port.ipc.h"
 
 namespace perfetto {
diff --git a/src/tracing/ipc/producer/BUILD.gn b/src/tracing/ipc/producer/BUILD.gn
new file mode 100644
index 0000000..c7e0d8a
--- /dev/null
+++ b/src/tracing/ipc/producer/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../gn/perfetto.gni")
+
+assert(enable_perfetto_ipc)
+
+# Posix specialization of the tracing library for Linux / Android / Mac.
+# Provides an IPC transport over a UNIX socket for the producer interface.
+source_set("producer") {
+  public_deps = [
+    "../../../../include/perfetto/ext/tracing/core",
+    "../../../../include/perfetto/ext/tracing/ipc",
+    "../../../../protos/perfetto/ipc",
+  ]
+  sources = [
+    "producer_ipc_client_impl.cc",
+    "producer_ipc_client_impl.h",
+  ]
+  deps = [
+    "..:common",
+    "../../../../gn:default_deps",
+    "../../../base",
+    "../../../ipc:client",
+  ]
+}
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 396bf4c..f06801a 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -358,9 +358,8 @@
                                                    buffer_exhausted_policy);
 }
 
-SharedMemoryArbiter* ProducerIPCClientImpl::GetInProcessShmemArbiter() {
-  PERFETTO_DLOG("Cannot GetInProcessShmemArbiter() via the IPC layer.");
-  return nullptr;
+SharedMemoryArbiter* ProducerIPCClientImpl::MaybeSharedMemoryArbiter() {
+  return shared_memory_arbiter_.get();
 }
 
 void ProducerIPCClientImpl::NotifyFlushComplete(FlushRequestID req_id) {
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.h b/src/tracing/ipc/producer/producer_ipc_client_impl.h
index e556fbb..68feeea 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.h
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.h
@@ -76,7 +76,7 @@
   std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer,
       BufferExhaustedPolicy) override;
-  SharedMemoryArbiter* GetInProcessShmemArbiter() override;
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override;
   void NotifyFlushComplete(FlushRequestID) override;
   SharedMemory* shared_memory() const override;
   size_t shared_buffer_page_size_kb() const override;
diff --git a/src/tracing/ipc/service/BUILD.gn b/src/tracing/ipc/service/BUILD.gn
new file mode 100644
index 0000000..0bc401c
--- /dev/null
+++ b/src/tracing/ipc/service/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../gn/perfetto.gni")
+
+assert(enable_perfetto_ipc)
+
+# Posix specialization of the tracing library for Linux / Android / Mac.
+# Provides an IPC transport over a UNIX socket for the service interface.
+source_set("service") {
+  public_deps = [
+    "../../../../include/perfetto/ext/tracing/core",
+    "../../../../include/perfetto/ext/tracing/ipc",
+  ]
+  sources = [
+    "consumer_ipc_service.cc",
+    "consumer_ipc_service.h",
+    "producer_ipc_service.cc",
+    "producer_ipc_service.h",
+    "service_ipc_host_impl.cc",
+    "service_ipc_host_impl.h",
+  ]
+  deps = [
+    "..:common",
+    "../../../../gn:default_deps",
+    "../../../../protos/perfetto/ipc",
+    "../../../base",
+    "../../../ipc:host",
+    "../../core:service",
+  ]
+}
diff --git a/test/task_runner_thread_delegates.cc b/src/tracing/platform_fake.cc
similarity index 66%
copy from test/task_runner_thread_delegates.cc
copy to src/tracing/platform_fake.cc
index 291482f..a13e7bf 100644
--- a/test/task_runner_thread_delegates.cc
+++ b/src/tracing/platform_fake.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-#include "test/task_runner_thread_delegates.h"
+#include "perfetto/tracing/platform.h"
 
 namespace perfetto {
 
-ServiceDelegate::~ServiceDelegate() = default;
-ProbesProducerDelegate::~ProbesProducerDelegate() = default;
-FakeProducerDelegate::~FakeProducerDelegate() = default;
+// This simply allows building the client library and using those parts of it
+// that do not depend on anything in the platform.
+
+// static
+Platform* Platform::GetDefaultPlatform() {
+  return nullptr;
+}
 
 }  // namespace perfetto
diff --git a/src/tracing/test/BUILD.gn b/src/tracing/test/BUILD.gn
index 2c99bc9..6a84532 100644
--- a/src/tracing/test/BUILD.gn
+++ b/src/tracing/test/BUILD.gn
@@ -12,6 +12,94 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("../../../gn/fuzzer.gni")
+import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
+
+perfetto_unittest_source_set("test_support") {
+  testonly = true
+  public_deps = [ "../core:test_support" ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../protos/perfetto/trace:cpp",
+    "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/ftrace:cpp",
+    "../../base",
+    "../../base:test_support",
+    "../core",
+    "../core:service",
+    "../core:test_support",
+  ]
+  sources = [
+    "aligned_buffer_test.cc",
+    "aligned_buffer_test.h",
+    "fake_packet.cc",
+    "fake_packet.h",
+    "test_shared_memory.cc",
+    "test_shared_memory.h",
+  ]
+
+  # These tests rely on test_task_runner.h which
+  # has no Windows implementation.
+  if (!is_win) {
+    sources += [
+      "fake_producer_endpoint.h",
+      "mock_consumer.cc",
+      "mock_consumer.h",
+      "mock_producer.cc",
+      "mock_producer.h",
+    ]
+  }
+}
+
+if (enable_perfetto_ipc) {
+  perfetto_unittest_source_set("tracing_integration_test") {
+    testonly = true
+    deps = [
+      ":test_support",
+      "../../../gn:default_deps",
+      "../../../gn:gtest_and_gmock",
+      "../../base",
+      "../../base:test_support",
+      "../core:service",
+      "../ipc/consumer",
+      "../ipc/producer",
+      "../ipc/service",
+    ]
+    sources = [ "tracing_integration_test.cc" ]
+  }
+}
+
+if (enable_perfetto_integration_tests) {
+  source_set("client_api_integrationtests") {
+    testonly = true
+    deps = [
+      ":api_test_support",
+      "../:client_api",
+      "../:platform_posix",
+      "../../../:libperfetto_client_experimental",
+      "../../../gn:default_deps",
+      "../../../gn:gtest_and_gmock",
+      "../../../include/perfetto/tracing/core",
+      "../../../protos/perfetto/trace:cpp",
+      "../../../protos/perfetto/trace:zero",
+      "../../../protos/perfetto/trace/interned_data:cpp",
+      "../../../protos/perfetto/trace/interned_data:zero",
+      "../../../protos/perfetto/trace/profiling:cpp",
+      "../../../protos/perfetto/trace/track_event:cpp",
+      "../../base",
+    ]
+    sources = [
+      "api_integrationtest.cc",
+      "tracing_module.cc",
+      "tracing_module.h",
+      "tracing_module2.cc",
+      "tracing_module_categories.h",
+    ]
+  }
+}
+
 # api_test_support needs to be self-contained and not leak any other perfetto
 # deps. See comment in api_test_support.h
 source_set("api_test_support") {
diff --git a/src/tracing/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
similarity index 78%
rename from src/tracing/api_integrationtest.cc
rename to src/tracing/test/api_integrationtest.cc
index a9f3fbb..b633582 100644
--- a/src/tracing/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -21,8 +21,12 @@
 #include <functional>
 #include <list>
 #include <mutex>
+#include <thread>
 #include <vector>
 
+// We also want to test legacy trace events.
+#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1
+
 #include "perfetto/tracing.h"
 #include "test/gtest_and_gmock.h"
 
@@ -50,20 +54,26 @@
 #include "protos/perfetto/trace/trace.gen.h"
 #include "protos/perfetto/trace/trace_packet.gen.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
+#include "protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/log_message.gen.h"
 #include "protos/perfetto/trace/track_event/log_message.pbzero.h"
 #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.gen.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
+#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
 #include "protos/perfetto/trace/track_event/track_event.gen.h"
 
 // Trace categories used in the tests.
 PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORY(test),
                            PERFETTO_CATEGORY(foo),
-                           PERFETTO_CATEGORY(bar));
+                           PERFETTO_CATEGORY(bar),
+                           PERFETTO_CATEGORY(cat));
 PERFETTO_TRACK_EVENT_STATIC_STORAGE();
 
 // For testing interning of complex objects.
@@ -228,6 +238,16 @@
   WaitableTestEvent on_stop;
 };
 
+class MyDebugAnnotation : public perfetto::DebugAnnotation {
+ public:
+  ~MyDebugAnnotation() override = default;
+
+  void Add(
+      perfetto::protos::pbzero::DebugAnnotation* annotation) const override {
+    annotation->set_legacy_json_value(R"({"key": 123})");
+  }
+};
+
 // -------------------------
 // Declaration of test class
 // -------------------------
@@ -237,16 +257,13 @@
 
   void SetUp() override {
     instance = this;
-    // Perfetto can only be initialized once in a process.
-    static bool was_initialized;
-    if (!was_initialized) {
-      perfetto::TracingInitArgs args;
-      args.backends = perfetto::kInProcessBackend;
-      perfetto::Tracing::Initialize(args);
-      was_initialized = true;
-      RegisterDataSource<MockDataSource>("my_data_source");
-      perfetto::TrackEvent::Register();
-    }
+
+    perfetto::TracingInitArgs args;
+    args.backends = perfetto::kInProcessBackend;
+    perfetto::Tracing::Initialize(args);
+    RegisterDataSource<MockDataSource>("my_data_source");
+    perfetto::TrackEvent::Register();
+
     // Make sure our data source always has a valid handle.
     data_sources_["my_data_source"];
   }
@@ -345,7 +362,8 @@
     bool incremental_state_was_cleared = false;
     uint32_t sequence_id = 0;
     for (const auto& packet : parsed_trace.packet()) {
-      if (packet.incremental_state_cleared()) {
+      if (packet.sequence_flags() & perfetto::protos::pbzero::TracePacket::
+                                        SEQ_INCREMENTAL_STATE_CLEARED) {
         incremental_state_was_cleared = true;
         categories.clear();
         event_names.clear();
@@ -391,12 +409,21 @@
         case perfetto::protos::gen::TrackEvent::TYPE_INSTANT:
           slice += "I";
           break;
-        default:
-        case perfetto::protos::gen::TrackEvent::TYPE_UNSPECIFIED:
+        case perfetto::protos::gen::TrackEvent::TYPE_UNSPECIFIED: {
+          EXPECT_TRUE(track_event.has_legacy_event());
           EXPECT_FALSE(track_event.type());
+          auto legacy_event = track_event.legacy_event();
+          slice += "Legacy_" +
+                   std::string(1, static_cast<char>(legacy_event.phase()));
+          break;
+        }
+        default:
+          ADD_FAILURE();
       }
-      slice += ":" + categories[track_event.category_iids()[0]] + "." +
-               event_names[track_event.name_iid()];
+      if (!track_event.category_iids().empty())
+        slice += ":" + categories[track_event.category_iids()[0]];
+      if (track_event.has_name_iid())
+        slice += "." + event_names[track_event.name_iid()];
 
       if (track_event.debug_annotations_size()) {
         slice += "(";
@@ -558,25 +585,21 @@
   bool begin_found = false;
   bool end_found = false;
   bool process_descriptor_found = false;
-  bool thread_descriptor_found = false;
   auto now = perfetto::test::GetTraceTimeNs();
   uint32_t sequence_id = 0;
   int32_t cur_pid = perfetto::test::GetCurrentProcessId();
   for (const auto& packet : trace.packet()) {
-    if (packet.has_process_descriptor()) {
-      EXPECT_FALSE(process_descriptor_found);
-      const auto& pd = packet.process_descriptor();
-      EXPECT_EQ(cur_pid, pd.pid());
-      process_descriptor_found = true;
+    if (packet.has_track_descriptor()) {
+      const auto& desc = packet.track_descriptor();
+      if (desc.has_process()) {
+        EXPECT_FALSE(process_descriptor_found);
+        const auto& pd = desc.process();
+        EXPECT_EQ(cur_pid, pd.pid());
+        process_descriptor_found = true;
+      }
     }
-    if (packet.has_thread_descriptor()) {
-      EXPECT_FALSE(thread_descriptor_found);
-      const auto& td = packet.thread_descriptor();
-      EXPECT_EQ(cur_pid, td.pid());
-      EXPECT_NE(0, td.tid());
-      thread_descriptor_found = true;
-    }
-    if (packet.incremental_state_cleared()) {
+    if (packet.sequence_flags() &
+        perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
       EXPECT_TRUE(packet.has_trace_packet_defaults());
       incremental_state_was_cleared = true;
       categories.clear();
@@ -585,6 +608,10 @@
 
     if (!packet.has_track_event())
       continue;
+    EXPECT_TRUE(
+        packet.sequence_flags() &
+        (perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED |
+         perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE));
     const auto& track_event = packet.track_event();
 
     // Make sure we only see track events on one sequence.
@@ -617,26 +644,24 @@
         perfetto::protos::pbzero::ClockSnapshot::Clock::MONOTONIC;
     EXPECT_EQ(packet.timestamp_clock_id(), kClockMonotonic);
 #endif
-    EXPECT_EQ(track_event.category_iids().size(), 1u);
-    EXPECT_GE(track_event.category_iids()[0], 1u);
-
     if (track_event.type() ==
         perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
       EXPECT_FALSE(begin_found);
+      EXPECT_EQ(track_event.category_iids().size(), 1u);
+      EXPECT_GE(track_event.category_iids()[0], 1u);
       EXPECT_EQ("test", categories[track_event.category_iids()[0]]);
       EXPECT_EQ("TestEvent", event_names[track_event.name_iid()]);
       begin_found = true;
     } else if (track_event.type() ==
                perfetto::protos::gen::TrackEvent::TYPE_SLICE_END) {
       EXPECT_FALSE(end_found);
+      EXPECT_EQ(track_event.category_iids().size(), 0u);
       EXPECT_EQ(0u, track_event.name_iid());
-      EXPECT_EQ("test", categories[track_event.category_iids()[0]]);
       end_found = true;
     }
   }
   EXPECT_TRUE(incremental_state_was_cleared);
   EXPECT_TRUE(process_descriptor_found);
-  EXPECT_TRUE(thread_descriptor_found);
   EXPECT_TRUE(begin_found);
   EXPECT_TRUE(end_found);
 }
@@ -828,6 +853,230 @@
   EXPECT_THAT(trace2, Not(HasSubstr("Session2_Third")));
 }
 
+TEST_F(PerfettoApiTest, TrackEventProcessAndThreadDescriptors) {
+  // Thread and process descriptors can be set before tracing is enabled.
+  perfetto::TrackEvent::SetProcessDescriptor(
+      [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+        desc->set_name("hello.exe");
+        desc->set_chrome_process()->set_process_priority(1);
+      });
+
+  // Erased tracks shouldn't show up anywhere.
+  perfetto::Track erased(1234u);
+  perfetto::TrackEvent::SetTrackDescriptor(
+      erased, [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+        desc->set_name("ErasedTrack");
+      });
+  perfetto::TrackEvent::EraseTrackDescriptor(erased);
+
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+  TRACE_EVENT_INSTANT("test", "MainThreadEvent");
+
+  std::thread thread([&] {
+    perfetto::TrackEvent::SetThreadDescriptor(
+        [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+          desc->set_name("TestThread");
+        });
+    TRACE_EVENT_INSTANT("test", "ThreadEvent");
+  });
+  thread.join();
+
+  // Update the process descriptor while tracing is enabled. It should be
+  // immediately reflected in the trace.
+  perfetto::TrackEvent::SetProcessDescriptor(
+      [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+        desc->set_name("goodbye.exe");
+      });
+  perfetto::TrackEvent::Flush();
+
+  tracing_session->get()->StopBlocking();
+
+  // After tracing ends, setting the descriptor has no immediate effect.
+  perfetto::TrackEvent::SetProcessDescriptor(
+      [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+        desc->set_name("noop.exe");
+      });
+
+  std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+  perfetto::protos::gen::Trace trace;
+  ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+  std::vector<perfetto::protos::gen::TrackDescriptor> descs;
+  std::vector<perfetto::protos::gen::TrackDescriptor> thread_descs;
+  constexpr uint32_t kMainThreadSequence = 2;
+  for (const auto& packet : trace.packet()) {
+    if (packet.has_track_descriptor()) {
+      if (packet.trusted_packet_sequence_id() == kMainThreadSequence) {
+        descs.push_back(packet.track_descriptor());
+      } else {
+        thread_descs.push_back(packet.track_descriptor());
+      }
+    }
+  }
+
+  // The main thread records the initial process name as well as the one that's
+  // set during tracing. Additionally it records a thread descriptor for the
+  // main thread.
+
+  EXPECT_EQ(3u, descs.size());
+
+  // Default track for the main thread.
+  EXPECT_EQ(0, descs[0].process().pid());
+  EXPECT_NE(0, descs[0].thread().pid());
+
+  // First process descriptor.
+  EXPECT_NE(0, descs[1].process().pid());
+  EXPECT_EQ("hello.exe", descs[1].name());
+
+  // Second process descriptor.
+  EXPECT_NE(0, descs[2].process().pid());
+  EXPECT_EQ("goodbye.exe", descs[2].name());
+
+  // The child thread records only its own thread descriptor (twice, since it
+  // was mutated).
+  EXPECT_EQ(2u, thread_descs.size());
+  EXPECT_EQ("TestThread", thread_descs[0].name());
+  EXPECT_NE(0, thread_descs[0].thread().pid());
+  EXPECT_NE(0, thread_descs[0].thread().tid());
+  EXPECT_EQ("TestThread", thread_descs[1].name());
+  EXPECT_NE(0, thread_descs[1].thread().pid());
+  EXPECT_NE(0, thread_descs[1].thread().tid());
+}
+
+TEST_F(PerfettoApiTest, TrackEventCustomTrack) {
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+  ds_cfg->set_legacy_config("bar");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  // Declare a custom track and give it a name.
+  uint64_t async_id = 123;
+  perfetto::TrackEvent::SetTrackDescriptor(
+      perfetto::Track(async_id),
+      [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+        desc->set_name("MyCustomTrack");
+      });
+
+  // Start events on one thread and end them on another.
+  TRACE_EVENT_BEGIN("bar", "AsyncEvent", perfetto::Track(async_id), "debug_arg",
+                    123);
+
+  TRACE_EVENT_BEGIN("bar", "SubEvent", perfetto::Track(async_id),
+                    [](perfetto::EventContext) {});
+  const auto main_thread_track =
+      perfetto::Track(async_id, perfetto::ThreadTrack::Current());
+  std::thread thread([&] {
+    TRACE_EVENT_END("bar", perfetto::Track(async_id));
+    TRACE_EVENT_END("bar", perfetto::Track(async_id), "arg1", false, "arg2",
+                    true);
+    const auto thread_track =
+        perfetto::Track(async_id, perfetto::ThreadTrack::Current());
+    // Thread-scoped tracks will have different uuids on different thread even
+    // if the id matches.
+    ASSERT_NE(main_thread_track.uuid, thread_track.uuid);
+  });
+  thread.join();
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+
+  std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+  perfetto::protos::gen::Trace trace;
+  ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+  // Check that the track uuids match on the begin and end events.
+  const auto track = perfetto::Track(async_id);
+  constexpr uint32_t kMainThreadSequence = 2;
+  int event_count = 0;
+  bool found_descriptor = false;
+  for (const auto& packet : trace.packet()) {
+    if (packet.has_track_descriptor() &&
+        !packet.track_descriptor().has_process() &&
+        !packet.track_descriptor().has_thread()) {
+      auto td = packet.track_descriptor();
+      EXPECT_EQ("MyCustomTrack", td.name());
+      EXPECT_EQ(track.uuid, td.uuid());
+      EXPECT_EQ(perfetto::ProcessTrack::Current().uuid, td.parent_uuid());
+      found_descriptor = true;
+      continue;
+    }
+
+    if (!packet.has_track_event())
+      continue;
+    auto track_event = packet.track_event();
+    if (track_event.type() ==
+        perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
+      EXPECT_EQ(kMainThreadSequence, packet.trusted_packet_sequence_id());
+      EXPECT_EQ(track.uuid, track_event.track_uuid());
+    } else {
+      EXPECT_NE(kMainThreadSequence, packet.trusted_packet_sequence_id());
+      EXPECT_EQ(track.uuid, track_event.track_uuid());
+    }
+    event_count++;
+  }
+  EXPECT_TRUE(found_descriptor);
+  EXPECT_EQ(4, event_count);
+  perfetto::TrackEvent::EraseTrackDescriptor(track);
+}
+
+TEST_F(PerfettoApiTest, TrackEventAnonymousCustomTrack) {
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+  ds_cfg->set_legacy_config("bar");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  // Emit an async event without giving it an explicit descriptor.
+  uint64_t async_id = 4004;
+  auto track = perfetto::Track(async_id, perfetto::ThreadTrack::Current());
+  TRACE_EVENT_BEGIN("bar", "AsyncEvent", track);
+  std::thread thread([&] { TRACE_EVENT_END("bar", track); });
+  thread.join();
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+
+  std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+  perfetto::protos::gen::Trace trace;
+  ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+  // Check that a descriptor for the track was emitted.
+  bool found_descriptor = false;
+  for (const auto& packet : trace.packet()) {
+    if (packet.has_track_descriptor() &&
+        !packet.track_descriptor().has_process() &&
+        !packet.track_descriptor().has_thread()) {
+      auto td = packet.track_descriptor();
+      EXPECT_EQ(track.uuid, td.uuid());
+      EXPECT_EQ(perfetto::ThreadTrack::Current().uuid, td.parent_uuid());
+      found_descriptor = true;
+    }
+  }
+  EXPECT_TRUE(found_descriptor);
+}
+
 TEST_F(PerfettoApiTest, TrackEventTypedArgs) {
   // Setup the trace config.
   perfetto::TraceConfig cfg;
@@ -1039,22 +1288,21 @@
   tracing_session->get()->StartBlocking();
 
   size_t body_iid;
-  TRACE_EVENT_BEGIN(
-      "foo", "EventWithState", [&](perfetto::EventContext ctx) {
-        // Test using a dynamically created interned value.
-        body_iid = InternedLogMessageBodyHashed::Get(
-            &ctx, std::string("Though this ") + "be madness,");
-        auto log = ctx.event()->set_log_message();
-        log->set_body_iid(body_iid);
+  TRACE_EVENT_BEGIN("foo", "EventWithState", [&](perfetto::EventContext ctx) {
+    // Test using a dynamically created interned value.
+    body_iid = InternedLogMessageBodyHashed::Get(
+        &ctx, std::string("Though this ") + "be madness,");
+    auto log = ctx.event()->set_log_message();
+    log->set_body_iid(body_iid);
 
-        auto body_iid2 =
-            InternedLogMessageBodyHashed::Get(&ctx, "Though this be madness,");
-        EXPECT_EQ(body_iid, body_iid2);
+    auto body_iid2 =
+        InternedLogMessageBodyHashed::Get(&ctx, "Though this be madness,");
+    EXPECT_EQ(body_iid, body_iid2);
 
-        auto body_iid3 =
-            InternedLogMessageBodyHashed::Get(&ctx, "yet there is method in’t");
-        EXPECT_NE(body_iid, body_iid3);
-      });
+    auto body_iid3 =
+        InternedLogMessageBodyHashed::Get(&ctx, "yet there is method in’t");
+    EXPECT_NE(body_iid, body_iid3);
+  });
   TRACE_EVENT_END("foo");
 
   tracing_session->get()->StopBlocking();
@@ -1151,10 +1399,10 @@
 
   tracing_session->get()->StopBlocking();
   auto slices = ReadSlicesFromTrace(tracing_session->get());
-  EXPECT_THAT(slices, ElementsAre("B:test.TestEventWithArgs", "E:test.",
-                                  "B:test.SingleLineTestEvent", "E:test.",
-                                  "B:test.TestEvent", "B:test.AnotherEvent",
-                                  "E:test.", "E:test."));
+  EXPECT_THAT(
+      slices,
+      ElementsAre("B:test.TestEventWithArgs", "E", "B:test.SingleLineTestEvent",
+                  "E", "B:test.TestEvent", "B:test.AnotherEvent", "E", "E"));
 }
 
 TEST_F(PerfettoApiTest, TrackEventInstant) {
@@ -1224,23 +1472,15 @@
   ds_cfg->set_name("track_event");
   ds_cfg->set_legacy_config("test");
 
-  class MyDebugAnnotation : public perfetto::DebugAnnotation {
-   public:
-    ~MyDebugAnnotation() override = default;
-
-    void Add(
-        perfetto::protos::pbzero::DebugAnnotation* annotation) const override {
-      annotation->set_legacy_json_value(R"({"key": 123})");
-    }
-  };
-
   // Create a new trace session.
   auto* tracing_session = NewTrace(cfg);
   tracing_session->get()->StartBlocking();
 
+  std::unique_ptr<MyDebugAnnotation> owned_annotation(new MyDebugAnnotation());
+
   TRACE_EVENT_BEGIN("test", "E", "custom_arg", MyDebugAnnotation());
   TRACE_EVENT_BEGIN("test", "E", "normal_arg", "x", "custom_arg",
-                    MyDebugAnnotation());
+                    std::move(owned_annotation));
   perfetto::TrackEvent::Flush();
 
   tracing_session->get()->StopBlocking();
@@ -1717,6 +1957,117 @@
   EXPECT_EQ(packets_found, 1 | 2 | 4 | 8);
 }
 
+TEST_F(PerfettoApiTest, LegacyTraceEvents) {
+  // TODO(skyostil): For now we just test that all variants of legacy trace
+  // points compile. Test actual functionality when implemented.
+
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  // Basic events.
+  TRACE_EVENT_INSTANT0("cat", "LegacyEvent", TRACE_EVENT_SCOPE_GLOBAL);
+  TRACE_EVENT_BEGIN1("cat", "LegacyEvent", "arg", 123);
+  TRACE_EVENT_END2("cat", "LegacyEvent", "arg", "string", "arg2", 0.123f);
+
+  // Scoped event.
+  { TRACE_EVENT0("cat", "ScopedLegacyEvent"); }
+
+  // Event with flow (and disabled category).
+  TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("cat"), "LegacyFlowEvent",
+                         0xdadacafe, TRACE_EVENT_FLAG_FLOW_IN);
+
+  // Event with timestamp.
+  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0("cat", "LegacyInstantEvent",
+                                      TRACE_EVENT_SCOPE_GLOBAL, 123456789ul);
+
+  // Event with id, thread id and timestamp (and dynamic name).
+  TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
+      "cat", std::string("LegacyWithIdTidAndTimestamp").c_str(), 1, 2, 3);
+
+  // Event with id.
+  TRACE_COUNTER1("cat", "LegacyCounter", 1234);
+  TRACE_COUNTER_ID1("cat", "LegacyCounterWithId", 1234, 9000);
+
+  // Metadata event.
+  TRACE_EVENT_METADATA1("cat", "LegacyMetadata", "obsolete", true);
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(
+      slices,
+      ElementsAre("I:cat.LegacyEvent", "B:cat.LegacyEvent(arg=(int)123)",
+                  "E.LegacyEvent(arg=(string)string,arg2=(double)0.123)",
+                  "B:cat.ScopedLegacyEvent", "E",
+                  "Legacy_C:cat.LegacyCounter(value=(int)1234)"));
+}
+
+TEST_F(PerfettoApiTest, LegacyTraceEventsWithCustomAnnotation) {
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  MyDebugAnnotation annotation;
+  TRACE_EVENT_BEGIN1("cat", "LegacyEvent", "arg", annotation);
+
+  std::unique_ptr<MyDebugAnnotation> owned_annotation(new MyDebugAnnotation());
+  TRACE_EVENT_BEGIN1("cat", "LegacyEvent", "arg", std::move(owned_annotation));
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(slices,
+              ElementsAre("B:cat.LegacyEvent(arg=(json){\"key\": 123})",
+                          "B:cat.LegacyEvent(arg=(json){\"key\": 123})"));
+}
+
+TEST_F(PerfettoApiTest, LegacyTraceEventsWithConcurrentSessions) {
+  // Make sure that a uniquely owned debug annotation can be written into
+  // multiple concurrent tracing sessions.
+
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  auto* tracing_session2 = NewTrace(cfg);
+  tracing_session2->get()->StartBlocking();
+
+  std::unique_ptr<MyDebugAnnotation> owned_annotation(new MyDebugAnnotation());
+  TRACE_EVENT_BEGIN1("cat", "LegacyEvent", "arg", std::move(owned_annotation));
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(slices,
+              ElementsAre("B:cat.LegacyEvent(arg=(json){\"key\": 123})"));
+
+  tracing_session2->get()->StopBlocking();
+  slices = ReadSlicesFromTrace(tracing_session2->get());
+  EXPECT_THAT(slices,
+              ElementsAre("B:cat.LegacyEvent(arg=(json){\"key\": 123})"));
+}
+
 }  // namespace
 
 PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(MockDataSource);
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 24d8d67..1667ffc 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -16,8 +16,8 @@
 
 #include "src/tracing/test/api_test_support.h"
 
+#include "perfetto/base/proc_utils.h"
 #include "perfetto/base/time.h"
-#include "perfetto/ext/base/proc_utils.h"
 
 namespace perfetto {
 namespace test {
diff --git a/src/tracing/test/fake_producer_endpoint.h b/src/tracing/test/fake_producer_endpoint.h
index fc364cf..c01ef91 100644
--- a/src/tracing/test/fake_producer_endpoint.h
+++ b/src/tracing/test/fake_producer_endpoint.h
@@ -44,7 +44,7 @@
       BufferExhaustedPolicy) override {
     return nullptr;
   }
-  SharedMemoryArbiter* GetInProcessShmemArbiter() override { return nullptr; }
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
 
   CommitDataRequest last_commit_data_request;
   CommitDataCallback last_commit_data_callback;
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index 09b6e5f..d4fef2e 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -82,4 +82,25 @@
   puts("Hello");
 }
 
+void FunctionWithOneTrackEventWithCustomTrack() {
+  TRACE_EVENT_BEGIN("cat1", "EventWithTrack", perfetto::Track(8086));
+  // Simulates the non-tracing work of this function, which should take priority
+  // over the above trace event in terms of instruction scheduling.
+  puts("Hello");
+}
+
+void FunctionWithOneLegacyEvent() {
+  TRACE_EVENT_BEGIN("cat1", "LegacyEventWithArgs", "arg1", 42, "arg2", .5f);
+  // Simulates the non-tracing work of this function, which should take priority
+  // over the above trace event in terms of instruction scheduling.
+  puts("Hello");
+}
+
+void FunctionWithOneScopedLegacyEvent() {
+  TRACE_EVENT("cat1", "ScopedLegacyEventWithArgs", "arg1", 42, "arg2", .5f);
+  // Simulates the non-tracing work of this function, which should take priority
+  // over the above trace event in terms of instruction scheduling.
+  puts("Hello");
+}
+
 }  // namespace tracing_module
diff --git a/src/tracing/test/tracing_module.h b/src/tracing/test/tracing_module.h
index e0fd1b3..7ed9364 100644
--- a/src/tracing/test/tracing_module.h
+++ b/src/tracing/test/tracing_module.h
@@ -37,6 +37,11 @@
 void FunctionWithOneTrackEventWithTypedArgument();
 void FunctionWithOneScopedTrackEvent();
 void FunctionWithOneTrackEventWithDebugAnnotations();
+void FunctionWithOneTrackEventWithCustomTrack();
+
+// Legacy events.
+void FunctionWithOneLegacyEvent();
+void FunctionWithOneScopedLegacyEvent();
 
 }  // namespace tracing_module
 
diff --git a/src/tracing/test/tracing_module_categories.h b/src/tracing/test/tracing_module_categories.h
index f776894..b492db8 100644
--- a/src/tracing/test/tracing_module_categories.h
+++ b/src/tracing/test/tracing_module_categories.h
@@ -23,6 +23,7 @@
 // categories can be written to the same trace writer.
 
 #define PERFETTO_TRACK_EVENT_NAMESPACE tracing_module
+#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1
 
 #include "perfetto/tracing.h"
 
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index fc6a696..8b20b7f 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -15,6 +15,7 @@
  */
 
 #include "perfetto/tracing/tracing.h"
+#include "perfetto/tracing/internal/track_event_internal.h"
 #include "src/tracing/internal/tracing_muxer_impl.h"
 
 #include <condition_variable>
@@ -24,9 +25,20 @@
 
 // static
 void Tracing::Initialize(const TracingInitArgs& args) {
+  static bool was_initialized = false;
+  static TracingInitArgs init_args;
+  if (was_initialized) {
+    // Should not be reinitialized with different args.
+    PERFETTO_DCHECK(init_args == args);
+    return;
+  }
+
   // Make sure the headers and implementation files agree on the build config.
   PERFETTO_CHECK(args.dcheck_is_on_ == PERFETTO_DCHECK_IS_ON());
   internal::TracingMuxerImpl::InitializeInstance(args);
+  internal::TrackRegistry::InitializeInstance();
+  was_initialized = true;
+  init_args = args;
 }
 
 //  static
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
new file mode 100644
index 0000000..b5260e4
--- /dev/null
+++ b/src/tracing/track.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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 "perfetto/tracing/track.h"
+
+#include "perfetto/ext/base/uuid.h"
+#include "perfetto/tracing/internal/track_event_data_source.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+
+namespace perfetto {
+
+// static
+uint64_t Track::process_uuid;
+
+void Track::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  desc->set_uuid(uuid);
+  if (parent_uuid)
+    desc->set_parent_uuid(parent_uuid);
+}
+
+void ProcessTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  Track::Serialize(desc);
+  auto pd = desc->set_process();
+  pd->set_pid(static_cast<int32_t>(pid));
+  // TODO(skyostil): Record command line.
+}
+
+void ThreadTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  Track::Serialize(desc);
+  auto td = desc->set_thread();
+  td->set_pid(static_cast<int32_t>(pid));
+  td->set_tid(static_cast<int32_t>(tid));
+  // TODO(skyostil): Record thread name.
+}
+
+namespace internal {
+
+// static
+TrackRegistry* TrackRegistry::instance_;
+
+TrackRegistry::TrackRegistry() = default;
+TrackRegistry::~TrackRegistry() = default;
+
+// static
+void TrackRegistry::InitializeInstance() {
+  // TODO(eseckler): Chrome may call this more than once. Once Chrome doesn't
+  // call this directly anymore, bring back DCHECK(!instance_) instead.
+  if (instance_)
+    return;
+  instance_ = new TrackRegistry();
+  Track::process_uuid = static_cast<uint64_t>(base::Uuidv4().lsb());
+}
+
+void TrackRegistry::UpdateTrackImpl(
+    Track track,
+    std::function<void(protos::pbzero::TrackDescriptor*)> fill_function) {
+  constexpr size_t kInitialSliceSize = 32;
+  constexpr size_t kMaximumSliceSize = 4096;
+  protozero::HeapBuffered<protos::pbzero::TrackDescriptor> new_descriptor(
+      kInitialSliceSize, kMaximumSliceSize);
+  fill_function(new_descriptor.get());
+  auto serialized_desc = new_descriptor.SerializeAsString();
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    tracks_[track.uuid] = std::move(serialized_desc);
+  }
+}
+
+void TrackRegistry::EraseTrack(Track track) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  tracks_.erase(track.uuid);
+}
+
+// static
+void TrackRegistry::WriteTrackDescriptor(
+    const SerializedTrackDescriptor& desc,
+    protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
+  packet->AppendString(
+      perfetto::protos::pbzero::TracePacket::kTrackDescriptorFieldNumber, desc);
+}
+
+}  // namespace internal
+}  // namespace perfetto
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 3388f02..7d16a3e 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -19,8 +19,6 @@
 source_set("end_to_end_integrationtests") {
   testonly = true
   deps = [
-    ":task_runner_thread",
-    ":task_runner_thread_delegates",
     ":test_helper",
     "../gn:default_deps",
     "../gn:gtest_and_gmock",
@@ -37,9 +35,7 @@
     "../src/base:test_support",
     "../src/traced/probes/ftrace",
   ]
-  sources = [
-    "end_to_end_integrationtest.cc",
-  ]
+  sources = [ "end_to_end_integrationtest.cc" ]
   if (start_daemons_for_testing) {
     cflags = [ "-DPERFETTO_START_DAEMONS_FOR_TESTING" ]
 
@@ -54,9 +50,7 @@
 }
 
 executable("client_api_example") {
-  sources = [
-    "client_api_example.cc",
-  ]
+  sources = [ "client_api_example.cc" ]
   deps = [
     "..:libperfetto_client_experimental",
     "../gn:default_deps",
@@ -68,27 +62,22 @@
 }
 
 perfetto_fuzzer_test("end_to_end_shared_memory_fuzzer") {
-  sources = [
-    "end_to_end_shared_memory_fuzzer.cc",
-  ]
+  sources = [ "end_to_end_shared_memory_fuzzer.cc" ]
   testonly = true
   deps = [
-    ":task_runner_thread",
-    ":task_runner_thread_delegates",
     ":test_helper",
     "../gn:default_deps",
     "../protos/perfetto/trace:zero",
     "../src/base:test_support",
     "../src/protozero",
-    "../src/tracing",
-    "../src/tracing:ipc",
+    "../src/tracing/core",
+    "../src/tracing/ipc/producer",
+    "../src/tracing/ipc/service",
   ]
 }
 
 perfetto_fuzzer_test("producer_socket_fuzzer") {
-  sources = [
-    "producer_socket_fuzzer.cc",
-  ]
+  sources = [ "producer_socket_fuzzer.cc" ]
   testonly = true
   deps = [
     ":test_helper",
@@ -98,53 +87,25 @@
   ]
 }
 
-source_set("task_runner_thread") {
-  testonly = true
-  deps = [
-    "../gn:default_deps",
-    "../src/base",
-    "../src/base:test_support",
-  ]
-  sources = [
-    "task_runner_thread.cc",
-    "task_runner_thread.h",
-  ]
-}
-
-source_set("task_runner_thread_delegates") {
-  testonly = true
-  deps = [
-    ":task_runner_thread",
-    "../gn:default_deps",
-    "../include/perfetto/ext/traced",
-    "../protos/perfetto/config:cpp",
-    "../src/base:test_support",
-    "../src/traced/probes:probes_src",
-    "../src/tracing:ipc",
-  ]
-  sources = [
-    "fake_producer.cc",
-    "fake_producer.h",
-    "task_runner_thread_delegates.cc",
-    "task_runner_thread_delegates.h",
-  ]
-}
-
 source_set("test_helper") {
   testonly = true
   public_deps = [
     "../protos/perfetto/trace:cpp",
-    "../src/tracing:ipc",
+    "../src/tracing/ipc/consumer",
+    "../src/tracing/ipc/producer",
+    "../src/tracing/ipc/service",
   ]
   deps = [
-    ":task_runner_thread",
-    ":task_runner_thread_delegates",
     "../gn:default_deps",
     "../include/perfetto/ext/traced",
+    "../protos/perfetto/config:cpp",
     "../protos/perfetto/trace:zero",
     "../src/base:test_support",
+    "../src/traced/probes:probes_src",
   ]
   sources = [
+    "fake_producer.cc",
+    "fake_producer.h",
     "test_helper.cc",
     "test_helper.h",
   ]
@@ -157,8 +118,6 @@
   source_set("end_to_end_benchmarks") {
     testonly = true
     deps = [
-      ":task_runner_thread",
-      ":task_runner_thread_delegates",
       ":test_helper",
       "../gn:benchmark",
       "../gn:default_deps",
@@ -169,9 +128,7 @@
       "../protos/perfetto/trace:zero",
       "../src/base:test_support",
     ]
-    sources = [
-      "end_to_end_benchmark.cc",
-    ]
+    sources = [ "end_to_end_benchmark.cc" ]
     if (start_daemons_for_testing) {
       cflags = [ "-DPERFETTO_START_DAEMONS_FOR_TESTING" ]
     }
@@ -183,8 +140,6 @@
       "../gn:benchmark",
       "../gn:default_deps",
     ]
-    sources = [
-      "benchmark_main.cc",
-    ]
+    sources = [ "benchmark_main.cc" ]
   }
 }  # if (enable_perfetto_benchmarks)
diff --git a/test/benchmark_main.cc b/test/benchmark_main.cc
index e410e52..3a3feaa 100644
--- a/test/benchmark_main.cc
+++ b/test/benchmark_main.cc
@@ -14,4 +14,4 @@
 
 #include <benchmark/benchmark.h>
 
-BENCHMARK_MAIN();
+BENCHMARK_MAIN()
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index 2ce30f6..0dc0d35 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -42,9 +42,7 @@
     "sys_stats.cfg",
   ]
 
-  outputs = [
-    "$root_out_dir/{{source_file_part}}.protobuf",
-  ]
+  outputs = [ "$root_out_dir/{{source_file_part}}.protobuf" ]
 
   # Retrieves the path where protoc is built relative to the dir where commands
   # are ran (root_build_dir == out/xxx). protoc_rel_dir ends up being "." for
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index 8ec9da3..aff1988 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -33,7 +33,6 @@
         <option name="append-bitness" value="true" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="setprop persist.traced.enable 1" />
         <option name="run-command" value="setprop persist.heapprofd.enable 1" />
         <option name="run-command" value="am start -n android.perfetto.producer/.ProducerActivity" />
     </target_preparer>
diff --git a/test/cts/BUILD.gn b/test/cts/BUILD.gn
index 4ecb26c..84aba0c 100644
--- a/test/cts/BUILD.gn
+++ b/test/cts/BUILD.gn
@@ -20,6 +20,7 @@
   complete_static_lib = true
   testonly = true
   deps = [
+    "..:end_to_end_integrationtests",
     "../..:libperfetto_client_experimental",
     "../../gn:default_deps",
     "../../gn:gtest_and_gmock",
@@ -39,3 +40,13 @@
     "utils.cc",
   ]
 }
+
+static_library("perfetto_cts_jni_deps") {
+  complete_static_lib = true
+  testonly = true
+  deps = [
+    "..:test_helper",
+    "../../gn:default_deps",
+    "../../src/base:test_support",
+  ]
+}
diff --git a/test/cts/producer/jni/fake_producer_jni.cc b/test/cts/producer/jni/fake_producer_jni.cc
index 47d4356..ab587a8 100644
--- a/test/cts/producer/jni/fake_producer_jni.cc
+++ b/test/cts/producer/jni/fake_producer_jni.cc
@@ -19,33 +19,99 @@
 #include "perfetto/ext/traced/traced.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 
-#include "src/base/test/test_task_runner.h"
+#include "perfetto/ext/base/unix_task_runner.h"
 
 #include "test/fake_producer.h"
 
+namespace {
+
+static std::mutex g_mutex;
+
+// These variables are guarded by the above mutex.
+static perfetto::base::UnixTaskRunner* g_activity_tr = nullptr;
+static perfetto::base::UnixTaskRunner* g_service_tr = nullptr;
+static perfetto::base::UnixTaskRunner* g_isolated_service_tr = nullptr;
+
+}  // namespace
+
 namespace perfetto {
 namespace {
-void ListenAndRespond(const std::string& name) {
-  base::TestTaskRunner task_runner;
+
+void ListenAndRespond(const std::string& name, base::UnixTaskRunner** tr) {
+  // Note that this lock is unlocked by a post task in the middle of the
+  // function instead of at the end of this function.
+  std::unique_lock<std::mutex> lock(g_mutex);
+
+  // Ensure that we don't create multiple instances of the same producer.
+  // If the passed task runner is non-null, that means it's still running
+  // so we don't need to create another instance.
+  if (*tr)
+    return;
+
+  // Post a task to unlock the mutex when the runner has started executing
+  // tasks.
+  base::UnixTaskRunner task_runner;
+  task_runner.PostTask([tr, &lock, &task_runner]() {
+    *tr = &task_runner;
+    lock.unlock();
+  });
+
   FakeProducer producer(name);
   producer.Connect(GetProducerSocket(), &task_runner, [] {}, [] {});
   task_runner.Run();
+
+  // Cleanup the task runner again to remove outside visibilty so we can
+  // create new instances of the producer.
+  {
+    std::lock_guard<std::mutex> guard(g_mutex);
+    *tr = nullptr;
+  }
 }
+
 }  // namespace
 }  // namespace perfetto
 
 extern "C" JNIEXPORT void JNICALL
+Java_android_perfetto_producer_ProducerActivity_quitTaskRunner(JNIEnv*,
+                                                               jclass) {
+  std::lock_guard<std::mutex> guard(g_mutex);
+  if (g_activity_tr) {
+    g_activity_tr->Quit();
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_perfetto_producer_ProducerIsolatedService_quitTaskRunner(JNIEnv*,
+                                                                      jclass) {
+  std::lock_guard<std::mutex> guard(g_mutex);
+  if (g_isolated_service_tr) {
+    g_isolated_service_tr->Quit();
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_perfetto_producer_ProducerService_quitTaskRunner(JNIEnv*, jclass) {
+  std::lock_guard<std::mutex> guard(g_mutex);
+  if (g_service_tr) {
+    g_service_tr->Quit();
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL
 Java_android_perfetto_producer_ProducerActivity_setupProducer(JNIEnv*, jclass) {
-  perfetto::ListenAndRespond("android.perfetto.cts.ProducerActivity");
+  perfetto::ListenAndRespond("android.perfetto.cts.ProducerActivity",
+                             &g_activity_tr);
 }
 
 extern "C" JNIEXPORT void JNICALL
 Java_android_perfetto_producer_ProducerIsolatedService_setupProducer(JNIEnv*,
                                                                      jclass) {
-  perfetto::ListenAndRespond("android.perfetto.cts.ProducerIsolatedService");
+  perfetto::ListenAndRespond("android.perfetto.cts.ProducerIsolatedService",
+                             &g_isolated_service_tr);
 }
 
 extern "C" JNIEXPORT void JNICALL
 Java_android_perfetto_producer_ProducerService_setupProducer(JNIEnv*, jclass) {
-  perfetto::ListenAndRespond("android.perfetto.cts.ProducerService");
+  perfetto::ListenAndRespond("android.perfetto.cts.ProducerService",
+                             &g_service_tr);
 }
diff --git a/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java b/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java
index 1eedfe3..5a8e0c9 100644
--- a/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java
+++ b/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java
@@ -50,6 +50,9 @@
         startForegroundService(new Intent(ProducerActivity.this, ProducerIsolatedService.class));
 
         System.loadLibrary("perfettocts_jni");
+
+        // We make sure at the C++ level that we don't setup multiple producers in the same
+        // process.
         new Thread(new Runnable() {
             public void run() {
                 try {
@@ -58,9 +61,17 @@
                     ex.printStackTrace();
                 }
             }
-        })
-                .start();
+        }).start();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        quitTaskRunner();
     }
 
     private static native void setupProducer();
+
+    private static native void quitTaskRunner();
 }
diff --git a/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java b/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java
index 9f26f54..943cab9 100644
--- a/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java
+++ b/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java
@@ -58,7 +58,11 @@
     }
 
     @Override
-    public void onDestroy() {}
+    public void onDestroy() {
+        quitTaskRunner();
+    }
 
     private static native void setupProducer();
+
+    private static native void quitTaskRunner();
 }
diff --git a/test/cts/producer/src/android/perfetto/producer/ProducerService.java b/test/cts/producer/src/android/perfetto/producer/ProducerService.java
index a86267a..3b1c3cf 100644
--- a/test/cts/producer/src/android/perfetto/producer/ProducerService.java
+++ b/test/cts/producer/src/android/perfetto/producer/ProducerService.java
@@ -57,7 +57,11 @@
     }
 
     @Override
-    public void onDestroy() {}
+    public void onDestroy() {
+        quitTaskRunner();
+    }
 
     private static native void setupProducer();
+
+    private static native void quitTaskRunner();
 }
diff --git a/test/end_to_end_benchmark.cc b/test/end_to_end_benchmark.cc
index 0668487..ff1fe74 100644
--- a/test/end_to_end_benchmark.cc
+++ b/test/end_to_end_benchmark.cc
@@ -22,8 +22,6 @@
 #include "perfetto/tracing/core/trace_config.h"
 #include "src/base/test/test_task_runner.h"
 #include "test/gtest_and_gmock.h"
-#include "test/task_runner_thread.h"
-#include "test/task_runner_thread_delegates.h"
 #include "test/test_helper.h"
 
 #include "protos/perfetto/config/test_config.gen.h"
@@ -73,8 +71,10 @@
   helper.WaitForProducerEnabled();
 
   uint64_t wall_start_ns = static_cast<uint64_t>(base::GetWallTimeNs().count());
-  uint64_t service_start_ns = helper.service_thread()->GetThreadCPUTimeNs();
-  uint64_t producer_start_ns = helper.producer_thread()->GetThreadCPUTimeNs();
+  uint64_t service_start_ns =
+      helper.service_thread()->GetThreadCPUTimeNsForTesting();
+  uint64_t producer_start_ns =
+      helper.producer_thread()->GetThreadCPUTimeNsForTesting();
   uint32_t iterations = 0;
   for (auto _ : state) {
     auto cname = "produced.and.committed." + std::to_string(iterations++);
@@ -83,9 +83,11 @@
     task_runner.RunUntilCheckpoint(cname, time_for_messages_ms);
   }
   uint64_t service_ns =
-      helper.service_thread()->GetThreadCPUTimeNs() - service_start_ns;
+      helper.service_thread()->GetThreadCPUTimeNsForTesting() -
+      service_start_ns;
   uint64_t producer_ns =
-      helper.producer_thread()->GetThreadCPUTimeNs() - producer_start_ns;
+      helper.producer_thread()->GetThreadCPUTimeNsForTesting() -
+      producer_start_ns;
   uint64_t wall_ns =
       static_cast<uint64_t>(base::GetWallTimeNs().count()) - wall_start_ns;
 
@@ -150,8 +152,8 @@
   helper.WaitForProducerEnabled();
 
   uint64_t wall_start_ns = static_cast<uint64_t>(base::GetWallTimeNs().count());
-  uint64_t service_start_ns =
-      static_cast<uint64_t>(helper.service_thread()->GetThreadCPUTimeNs());
+  uint64_t service_start_ns = static_cast<uint64_t>(
+      helper.service_thread()->GetThreadCPUTimeNsForTesting());
   uint64_t consumer_start_ns =
       static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
   uint64_t read_time_taken_ns = 0;
@@ -193,7 +195,8 @@
     }
   }
   uint64_t service_ns =
-      helper.service_thread()->GetThreadCPUTimeNs() - service_start_ns;
+      helper.service_thread()->GetThreadCPUTimeNsForTesting() -
+      service_start_ns;
   uint64_t consumer_ns =
       static_cast<uint64_t>(base::GetThreadCPUTimeNs().count()) -
       consumer_start_ns;
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 209693d..52947df 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -35,8 +35,6 @@
 #include "src/traced/probes/ftrace/ftrace_controller.h"
 #include "src/traced/probes/ftrace/ftrace_procfs.h"
 #include "test/gtest_and_gmock.h"
-#include "test/task_runner_thread.h"
-#include "test/task_runner_thread_delegates.h"
 #include "test/test_helper.h"
 
 #include "protos/perfetto/config/power/android_power_config.pbzero.h"
@@ -362,9 +360,8 @@
   helper.StartServiceIfRequired();
 
 #if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  TaskRunnerThread producer_thread("perfetto.prd");
-  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
-      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
+  ProbesProducerThread probes(TEST_PRODUCER_SOCK_NAME);
+  probes.Connect();
 #endif
 
   helper.ConnectConsumer();
@@ -408,9 +405,8 @@
   helper.StartServiceIfRequired();
 
 #if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  TaskRunnerThread producer_thread("perfetto.prd");
-  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
-      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
+  ProbesProducerThread probes(TEST_PRODUCER_SOCK_NAME);
+  probes.Connect();
 #endif
 
   helper.ConnectConsumer();
@@ -467,9 +463,8 @@
   helper.StartServiceIfRequired();
 
 #if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  TaskRunnerThread producer_thread("perfetto.prd");
-  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
-      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
+  ProbesProducerThread probes(TEST_PRODUCER_SOCK_NAME);
+  probes.Connect();
 #else
   base::ignore_result(TEST_PRODUCER_SOCK_NAME);
 #endif
diff --git a/test/end_to_end_shared_memory_fuzzer.cc b/test/end_to_end_shared_memory_fuzzer.cc
index 052f888..ce53acf 100644
--- a/test/end_to_end_shared_memory_fuzzer.cc
+++ b/test/end_to_end_shared_memory_fuzzer.cc
@@ -30,8 +30,6 @@
 #include "perfetto/tracing/core/data_source_descriptor.h"
 #include "protos/perfetto/trace/test_event.pbzero.h"
 #include "src/base/test/test_task_runner.h"
-#include "test/task_runner_thread.h"
-#include "test/task_runner_thread_delegates.h"
 #include "test/test_helper.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -109,23 +107,33 @@
   std::function<void()> on_produced_and_committed_;
 };
 
-class FakeProducerDelegate : public ThreadDelegate {
+class FuzzerFakeProducerThread {
  public:
-  FakeProducerDelegate(const uint8_t* data,
-                       size_t size,
-                       std::function<void()> on_produced_and_committed)
+  FuzzerFakeProducerThread(const uint8_t* data,
+                           size_t size,
+                           std::function<void()> on_produced_and_committed)
       : data_(data),
         size_(size),
         on_produced_and_committed_(on_produced_and_committed) {}
-  ~FakeProducerDelegate() override = default;
 
-  void Initialize(base::TaskRunner* task_runner) override {
-    producer_.reset(new FakeProducer("android.perfetto.FakeProducer", data_,
-                                     size_, on_produced_and_committed_));
-    producer_->Connect(TEST_PRODUCER_SOCK_NAME, task_runner);
+  ~FuzzerFakeProducerThread() {
+    if (!runner_)
+      return;
+    runner_->PostTaskAndWaitForTesting([this]() { producer_.reset(); });
+  }
+
+  void Connect() {
+    runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.prd.fake");
+    runner_->PostTaskAndWaitForTesting([this]() {
+      producer_.reset(new FakeProducer("android.perfetto.FakeProducer", data_,
+                                       size_, on_produced_and_committed_));
+      producer_->Connect(TEST_PRODUCER_SOCK_NAME, runner_->get());
+    });
   }
 
  private:
+  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+
   std::unique_ptr<FakeProducer> producer_;
   const uint8_t* data_;
   const size_t size_;
@@ -140,11 +148,10 @@
   TestHelper helper(&task_runner);
   helper.StartServiceIfRequired();
 
-  TaskRunnerThread producer_thread("perfetto.prd");
-  producer_thread.Start(std::unique_ptr<FakeProducerDelegate>(
-      new FakeProducerDelegate(data, size,
-                               helper.WrapTask(task_runner.CreateCheckpoint(
-                                   "produced.and.committed")))));
+  auto cp =
+      helper.WrapTask(task_runner.CreateCheckpoint("produced.and.committed"));
+  FuzzerFakeProducerThread producer_thread(data, size, cp);
+  producer_thread.Connect();
 
   helper.ConnectConsumer();
   helper.WaitForConsumerConnect();
diff --git a/test/fake_producer.cc b/test/fake_producer.cc
index 17f80f0..730ec00 100644
--- a/test/fake_producer.cc
+++ b/test/fake_producer.cc
@@ -16,7 +16,6 @@
 
 #include "test/fake_producer.h"
 
-#include <condition_variable>
 #include <mutex>
 
 #include "perfetto/base/logging.h"
diff --git a/test/metrics/java_heap_stats.out b/test/metrics/java_heap_stats.out
index 8ba3b96..53b9fb6 100644
--- a/test/metrics/java_heap_stats.out
+++ b/test/metrics/java_heap_stats.out
@@ -7,7 +7,7 @@
     }
     samples {
       ts: 10
-      heap_size: 480
+      heap_size: 736
       reachable_heap_size: 96
     }
   }
diff --git a/test/metrics/trace_metadata.json.out b/test/metrics/trace_metadata.json.out
index c961b9e..031db4c 100644
--- a/test/metrics/trace_metadata.json.out
+++ b/test/metrics/trace_metadata.json.out
@@ -6,6 +6,7 @@
         "value": 5
       }
     ],
-    "trace_duration_ns": 9519159074
+    "trace_duration_ns": 9519159074,
+    "trace_size_bytes": 6365447
   }
 }
diff --git a/test/metrics/trace_metadata.out b/test/metrics/trace_metadata.out
index c5ca0d8..80d5bad 100644
--- a/test/metrics/trace_metadata.out
+++ b/test/metrics/trace_metadata.out
@@ -3,5 +3,6 @@
     name: "mismatched_sched_switch_tids"
     value: 5
   }
-  trace_duration_ns: 9519159074
+  trace_duration_ns: 9519159074,
+  trace_size_bytes: 6365447
 }
diff --git a/test/synth_common.py b/test/synth_common.py
index f950294..036f9b7 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -19,6 +19,8 @@
 from google.protobuf.pyext import _message
 
 CLONE_THREAD = 0x00010000
+CLONE_VFORK = 0x00004000
+CLONE_VM = 0x00000100
 
 
 class Trace(object):
@@ -342,6 +344,18 @@
     debug_marker.object = obj
     debug_marker.object_name = obj_name
 
+  def add_vk_queue_submit(self, ts, dur, pid, tid, vk_queue, vk_command_buffers,
+                          submission_id):
+    packet = self.add_packet()
+    packet.timestamp = ts
+    submit = (self.packet.vulkan_api_event.vk_queue_submit)
+    submit.duration_ns = dur
+    submit.pid = pid
+    submit.tid = tid
+    for cmd in vk_command_buffers:
+      submit.vk_command_buffers.append(cmd)
+    submit.submission_id = submission_id
+
   def add_gpu_log(self, ts, severity, tag, message):
     packet = self.add_packet()
     packet.timestamp = ts
diff --git a/test/task_runner_thread.cc b/test/task_runner_thread.cc
deleted file mode 100644
index c625078..0000000
--- a/test/task_runner_thread.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2018 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 <pthread.h>
-#include <stdlib.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <condition_variable>
-#include <thread>
-
-#include "perfetto/base/time.h"
-#include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/string_splitter.h"
-#include "test/task_runner_thread.h"
-
-namespace perfetto {
-
-TaskRunnerThread::TaskRunnerThread(const char* name) : name_(name) {}
-TaskRunnerThread::~TaskRunnerThread() {
-  Stop();
-}
-
-void TaskRunnerThread::Start(std::unique_ptr<ThreadDelegate> delegate) {
-  // Begin holding the lock for the condition variable.
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  // Start the thread.
-  PERFETTO_DCHECK(!runner_);
-  thread_ = std::thread(&TaskRunnerThread::Run, this, std::move(delegate));
-
-  // Wait for runner to be ready.
-  ready_.wait_for(lock, std::chrono::seconds(10),
-                  [this]() { return runner_ != nullptr; });
-}
-
-void TaskRunnerThread::Stop() {
-  {
-    std::unique_lock<std::mutex> lock(mutex_);
-    if (runner_)
-      runner_->Quit();
-  }
-
-  if (thread_.joinable())
-    thread_.join();
-}
-
-uint64_t TaskRunnerThread::GetThreadCPUTimeNs() {
-  std::condition_variable cv;
-  std::unique_lock<std::mutex> lock(mutex_);
-  uint64_t thread_time_ns = 0;
-
-  if (!runner_)
-    return 0;
-
-  runner_->PostTask([this, &thread_time_ns, &cv] {
-    std::unique_lock<std::mutex> inner_lock(mutex_);
-    thread_time_ns = static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
-    cv.notify_one();
-  });
-
-  cv.wait(lock, [&thread_time_ns] { return thread_time_ns != 0; });
-  return thread_time_ns;
-}
-
-void TaskRunnerThread::Run(std::unique_ptr<ThreadDelegate> delegate) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
-  pthread_setname_np(name_);
-#else
-  pthread_setname_np(pthread_self(), name_);
-#endif
-
-  // Create the task runner and execute the specicalised code.
-  base::UnixTaskRunner task_runner;
-  delegate->Initialize(&task_runner);
-
-  // Pass the runner back to the main thread.
-  {
-    std::unique_lock<std::mutex> lock(mutex_);
-    runner_ = &task_runner;
-  }
-
-  // Notify the main thread that the runner is ready.
-  ready_.notify_one();
-
-  // Spin the loop.
-  task_runner.Run();
-
-  // Ensure we clear out the delegate before runner goes out
-  // of scope.
-  delegate.reset();
-
-  // Cleanup the runner.
-  {
-    std::unique_lock<std::mutex> lock(mutex_);
-    runner_ = nullptr;
-  }
-}
-
-ThreadDelegate::~ThreadDelegate() = default;
-
-}  // namespace perfetto
diff --git a/test/task_runner_thread.h b/test/task_runner_thread.h
deleted file mode 100644
index 354c308..0000000
--- a/test/task_runner_thread.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 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 TEST_TASK_RUNNER_THREAD_H_
-#define TEST_TASK_RUNNER_THREAD_H_
-
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
-#include "perfetto/base/task_runner.h"
-#include "src/base/test/test_task_runner.h"
-
-namespace perfetto {
-
-// Used to perform initialization work on a background TaskRunnerThread.
-class ThreadDelegate {
- public:
-  virtual ~ThreadDelegate();
-
-  // Invoked on the target thread before the message loop is started.
-  virtual void Initialize(base::TaskRunner* task_runner) = 0;
-};
-
-// Background thread which spins a task runner until completed or the thread is
-// destroyed. If the thread is destroyed before the task runner completes, the
-// task runner is quit and the thread is joined.
-class TaskRunnerThread {
- public:
-  explicit TaskRunnerThread(const char* name);
-  ~TaskRunnerThread();
-
-  // Blocks until the thread has been created and Initialize() has been
-  // called.
-  void Start(std::unique_ptr<ThreadDelegate> delegate);
-
-  // Blocks until the thread has been stopped and joined.
-  void Stop();
-
-  uint64_t GetThreadCPUTimeNs();
-
- private:
-  void Run(std::unique_ptr<ThreadDelegate> delegate);
-
-  const char* const name_;
-  std::thread thread_;
-  std::condition_variable ready_;
-
-  // All variables below this point are protected by |mutex_|.
-  std::mutex mutex_;
-  base::UnixTaskRunner* runner_ = nullptr;
-};
-
-}  // namespace perfetto
-
-#endif  // TEST_TASK_RUNNER_THREAD_H_
diff --git a/test/task_runner_thread_delegates.h b/test/task_runner_thread_delegates.h
deleted file mode 100644
index bea384a9..0000000
--- a/test/task_runner_thread_delegates.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2018 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 TEST_TASK_RUNNER_THREAD_DELEGATES_H_
-#define TEST_TASK_RUNNER_THREAD_DELEGATES_H_
-
-#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
-#include "src/traced/probes/probes_producer.h"
-#include "test/fake_producer.h"
-#include "test/task_runner_thread.h"
-
-namespace perfetto {
-// This is used only in daemon starting integrations tests.
-class ServiceDelegate : public ThreadDelegate {
- public:
-  ServiceDelegate(const std::string& producer_socket,
-                  const std::string& consumer_socket)
-      : producer_socket_(producer_socket), consumer_socket_(consumer_socket) {}
-  ~ServiceDelegate() override;
-
-  void Initialize(base::TaskRunner* task_runner) override {
-    svc_ = ServiceIPCHost::CreateInstance(task_runner);
-    unlink(producer_socket_.c_str());
-    unlink(consumer_socket_.c_str());
-    svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
-  }
-
- private:
-  std::string producer_socket_;
-  std::string consumer_socket_;
-  std::unique_ptr<ServiceIPCHost> svc_;
-};
-
-// This is used only in daemon starting integrations tests.
-class ProbesProducerDelegate : public ThreadDelegate {
- public:
-  ProbesProducerDelegate(const std::string& producer_socket)
-      : producer_socket_(producer_socket) {}
-  ~ProbesProducerDelegate() override;
-
-  void Initialize(base::TaskRunner* task_runner) override {
-    producer_.reset(new ProbesProducer);
-    producer_->ConnectWithRetries(producer_socket_.c_str(), task_runner);
-  }
-
- private:
-  std::string producer_socket_;
-  std::unique_ptr<ProbesProducer> producer_;
-};
-
-class FakeProducerDelegate : public ThreadDelegate {
- public:
-  FakeProducerDelegate(const std::string& producer_socket,
-                       std::function<void()> setup_callback,
-                       std::function<void()> connect_callback)
-      : producer_socket_(producer_socket),
-        setup_callback_(std::move(setup_callback)),
-        connect_callback_(std::move(connect_callback)) {}
-  ~FakeProducerDelegate() override;
-
-  void Initialize(base::TaskRunner* task_runner) override {
-    producer_.reset(new FakeProducer("android.perfetto.FakeProducer"));
-    producer_->Connect(producer_socket_.c_str(), task_runner,
-                       std::move(setup_callback_),
-                       std::move(connect_callback_));
-  }
-
-  FakeProducer* producer() { return producer_.get(); }
-
- private:
-  std::string producer_socket_;
-  std::unique_ptr<FakeProducer> producer_;
-  std::function<void()> setup_callback_;
-  std::function<void()> connect_callback_;
-};
-}  // namespace perfetto
-
-#endif  // TEST_TASK_RUNNER_THREAD_DELEGATES_H_
diff --git a/test/test_helper.cc b/test/test_helper.cc
index e716c07..51d9cb7 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -18,7 +18,6 @@
 
 #include "perfetto/ext/traced/traced.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
-#include "test/task_runner_thread_delegates.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -41,8 +40,10 @@
 TestHelper::TestHelper(base::TestTaskRunner* task_runner)
     : instance_num_(next_instance_num_++),
       task_runner_(task_runner),
-      service_thread_("perfetto.svc"),
-      producer_thread_("perfetto.prd") {}
+      service_thread_(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME),
+      fake_producer_thread_(TEST_PRODUCER_SOCK_NAME,
+                            WrapTask(CreateCheckpoint("producer.setup")),
+                            WrapTask(CreateCheckpoint("producer.enabled"))) {}
 
 void TestHelper::OnConnect() {
   std::move(on_connect_callback_)();
@@ -77,19 +78,13 @@
 
 void TestHelper::StartServiceIfRequired() {
 #if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  service_thread_.Start(std::unique_ptr<ServiceDelegate>(
-      new ServiceDelegate(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME)));
+  service_thread_.Start();
 #endif
 }
 
 FakeProducer* TestHelper::ConnectFakeProducer() {
-  std::unique_ptr<FakeProducerDelegate> producer_delegate(
-      new FakeProducerDelegate(TEST_PRODUCER_SOCK_NAME,
-                               WrapTask(CreateCheckpoint("producer.setup")),
-                               WrapTask(CreateCheckpoint("producer.enabled"))));
-  FakeProducerDelegate* producer_delegate_cached = producer_delegate.get();
-  producer_thread_.Start(std::move(producer_delegate));
-  return producer_delegate_cached->producer();
+  fake_producer_thread_.Connect();
+  return fake_producer_thread_.producer();
 }
 
 void TestHelper::ConnectConsumer() {
diff --git a/test/test_helper.h b/test/test_helper.h
index 11897e5..e18531e 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -18,18 +18,121 @@
 #define TEST_TEST_HELPER_H_
 
 #include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/tracing/core/consumer.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
 #include "perfetto/tracing/core/trace_config.h"
 #include "src/base/test/test_task_runner.h"
+#include "src/traced/probes/probes_producer.h"
 #include "test/fake_producer.h"
-#include "test/task_runner_thread.h"
 
 #include "protos/perfetto/trace/trace_packet.gen.h"
 
 namespace perfetto {
 
+// This is used only in daemon starting integrations tests.
+class ServiceThread {
+ public:
+  ServiceThread(const std::string& producer_socket,
+                const std::string& consumer_socket)
+      : producer_socket_(producer_socket), consumer_socket_(consumer_socket) {}
+
+  ~ServiceThread() {
+    if (!runner_)
+      return;
+    runner_->PostTaskAndWaitForTesting([this]() { svc_.reset(); });
+  }
+
+  void Start() {
+    runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.svc");
+    runner_->PostTaskAndWaitForTesting([this]() {
+      svc_ = ServiceIPCHost::CreateInstance(runner_->get());
+      unlink(producer_socket_.c_str());
+      unlink(consumer_socket_.c_str());
+
+      bool res =
+          svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
+      PERFETTO_CHECK(res);
+    });
+  }
+
+  base::ThreadTaskRunner* runner() { return runner_ ? &*runner_ : nullptr; }
+
+ private:
+  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+
+  std::string producer_socket_;
+  std::string consumer_socket_;
+  std::unique_ptr<ServiceIPCHost> svc_;
+};
+
+// This is used only in daemon starting integrations tests.
+class ProbesProducerThread {
+ public:
+  ProbesProducerThread(const std::string& producer_socket)
+      : producer_socket_(producer_socket) {}
+
+  ~ProbesProducerThread() {
+    if (!runner_)
+      return;
+    runner_->PostTaskAndWaitForTesting([this]() { producer_.reset(); });
+  }
+
+  void Connect() {
+    runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.prd.probes");
+    runner_->PostTaskAndWaitForTesting([this]() {
+      producer_.reset(new ProbesProducer());
+      producer_->ConnectWithRetries(producer_socket_.c_str(), runner_->get());
+    });
+  }
+
+ private:
+  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+
+  std::string producer_socket_;
+  std::unique_ptr<ProbesProducer> producer_;
+};
+
+class FakeProducerThread {
+ public:
+  FakeProducerThread(const std::string& producer_socket,
+                     std::function<void()> setup_callback,
+                     std::function<void()> connect_callback)
+      : producer_socket_(producer_socket),
+        setup_callback_(std::move(setup_callback)),
+        connect_callback_(std::move(connect_callback)) {}
+
+  ~FakeProducerThread() {
+    if (!runner_)
+      return;
+    runner_->PostTaskAndWaitForTesting([this]() { producer_.reset(); });
+  }
+
+  void Connect() {
+    runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.prd.fake");
+    runner_->PostTaskAndWaitForTesting([this]() {
+      producer_.reset(new FakeProducer("android.perfetto.FakeProducer"));
+      producer_->Connect(producer_socket_.c_str(), runner_->get(),
+                         std::move(setup_callback_),
+                         std::move(connect_callback_));
+    });
+  }
+
+  base::ThreadTaskRunner* runner() { return runner_ ? &*runner_ : nullptr; }
+
+  FakeProducer* producer() { return producer_.get(); }
+
+ private:
+  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+
+  std::string producer_socket_;
+  std::unique_ptr<FakeProducer> producer_;
+  std::function<void()> setup_callback_;
+  std::function<void()> connect_callback_;
+};
+
 class TestHelper : public Consumer {
  public:
   static const char* GetConsumerSocketName();
@@ -79,8 +182,10 @@
 
   std::function<void()> WrapTask(const std::function<void()>& function);
 
-  TaskRunnerThread* service_thread() { return &service_thread_; }
-  TaskRunnerThread* producer_thread() { return &producer_thread_; }
+  base::ThreadTaskRunner* service_thread() { return service_thread_.runner(); }
+  base::ThreadTaskRunner* producer_thread() {
+    return fake_producer_thread_.runner();
+  }
   const std::vector<protos::gen::TracePacket>& trace() { return trace_; }
 
  private:
@@ -97,8 +202,9 @@
 
   std::vector<protos::gen::TracePacket> trace_;
 
-  TaskRunnerThread service_thread_;
-  TaskRunnerThread producer_thread_;
+  ServiceThread service_thread_;
+  FakeProducerThread fake_producer_thread_;
+
   std::unique_ptr<TracingService::ConsumerEndpoint> endpoint_;  // Keep last.
 };
 
diff --git a/test/trace_processor/android_sched_and_ps_trace_size.out b/test/trace_processor/android_sched_and_ps_trace_size.out
new file mode 100644
index 0000000..b2bd2ea
--- /dev/null
+++ b/test/trace_processor/android_sched_and_ps_trace_size.out
@@ -0,0 +1,2 @@
+"int_value"
+18761615
diff --git a/test/trace_processor/fuchsia_workstation_smoke_slices.out b/test/trace_processor/fuchsia_workstation_smoke_slices.out
index 496d5ec..2d133e8 100644
--- a/test/trace_processor/fuchsia_workstation_smoke_slices.out
+++ b/test/trace_processor/fuchsia_workstation_smoke_slices.out
@@ -19,6 +19,6 @@
 "thread_track",17,38
 "thread_track",18,12
 "thread_track",19,1
-"track",0,84
+"track",0,85
 "track",1,14
-"track",2,2
+"track",2,1
diff --git a/test/trace_processor/gpu_render_stages.out b/test/trace_processor/gpu_render_stages.out
index ace66ec..0cfaaf6 100644
--- a/test/trace_processor/gpu_render_stages.out
+++ b/test/trace_processor/gpu_render_stages.out
@@ -1,17 +1,18 @@
-"track_name","track_desc","ts","dur","slice_name","depth","flat_key","string_value","context_id","render_target","submission_id","hw_queue_id"
-"queue 1","queue desc 1",0,5,"render stage(1)",0,"[NULL]","[NULL]",0,0,0,1
-"queue 0","queue desc 0",0,5,"stage 0",0,"[NULL]","[NULL]",42,0,0,0
-"queue 1","queue desc 1",10,5,"stage 1",0,"description","stage desc 1",42,0,0,1
-"queue 2","[NULL]",20,5,"stage 2",0,"[NULL]","[NULL]",42,0,0,2
-"queue 0","queue desc 0",30,5,"stage 3",0,"[NULL]","[NULL]",42,0,0,0
-"Unknown GPU Queue 3","[NULL]",40,5,"render stage(4)",0,"[NULL]","[NULL]",42,0,0,3
-"queue 0","queue desc 0",50,5,"stage 0",0,"key1","value1",42,0,0,0
-"queue 0","queue desc 0",60,5,"stage 0",0,"key2","value2",42,0,0,0
-"queue 0","queue desc 0",60,5,"stage 0",0,"key1","value1",42,0,0,0
-"queue 0","queue desc 0",70,5,"stage 0",0,"key1","[NULL]",42,0,0,0
-"queue 0","queue desc 0",80,5,"stage 2",0,"[NULL]","[NULL]",42,0,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"[NULL]","[NULL]",42,16,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"[NULL]","[NULL]",42,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[framebuffer]",0,"[NULL]","[NULL]",42,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[renamed_buffer]",0,"[NULL]","[NULL]",42,16,0,0
-"Unknown GPU Queue ","[NULL]",120,5,"render stage(18446744073709551615)",0,"[NULL]","[NULL]",42,0,0,1024
+"track_name","track_desc","ts","dur","slice_name","depth","flat_key","string_value","context_id","render_target","render_target_name","render_pass","render_pass_name","command_buffer","command_buffer_name","submission_id","hw_queue_id"
+"queue 1","queue desc 1",0,5,"render stage(1)",0,"[NULL]","[NULL]",0,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,1
+"queue 0","queue desc 0",0,5,"stage 0",0,"[NULL]","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"queue 1","queue desc 1",10,5,"stage 1",0,"description","stage desc 1",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,1
+"queue 2","[NULL]",20,5,"stage 2",0,"[NULL]","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,2
+"queue 0","queue desc 0",30,5,"stage 3",0,"[NULL]","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"Unknown GPU Queue 3","[NULL]",40,5,"render stage(4)",0,"[NULL]","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,3
+"queue 0","queue desc 0",50,5,"stage 0",0,"key1","value1",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"queue 0","queue desc 0",60,5,"stage 0",0,"key2","value2",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"queue 0","queue desc 0",60,5,"stage 0",0,"key1","value1",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"queue 0","queue desc 0",70,5,"stage 0",0,"key1","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"queue 0","queue desc 0",80,5,"stage 2",0,"[NULL]","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,0
+"queue 0","queue desc 0",90,5,"stage 0",0,"[NULL]","[NULL]",42,16,"[NULL]",32,"[NULL]",48,"[NULL]",0,0
+"queue 0","queue desc 0",100,5,"stage 0",0,"[NULL]","[NULL]",42,16,"[NULL]",16,"[NULL]",16,"command_buffer",0,0
+"queue 0","queue desc 0",110,5,"stage 0",0,"[NULL]","[NULL]",42,16,"[NULL]",16,"render_pass",16,"command_buffer",0,0
+"queue 0","queue desc 0",120,5,"stage 0",0,"[NULL]","[NULL]",42,16,"framebuffer",16,"render_pass",16,"command_buffer",0,0
+"queue 0","queue desc 0",130,5,"stage 0",0,"[NULL]","[NULL]",42,16,"renamed_buffer",0,"[NULL]",0,"[NULL]",0,0
+"Unknown GPU Queue ","[NULL]",140,5,"render stage(18446744073709551615)",0,"[NULL]","[NULL]",42,0,"[NULL]",0,"[NULL]",0,"[NULL]",0,1024
diff --git a/test/trace_processor/gpu_render_stages.py b/test/trace_processor/gpu_render_stages.py
index 48c6866..a88fc78 100644
--- a/test/trace_processor/gpu_render_stages.py
+++ b/test/trace_processor/gpu_render_stages.py
@@ -93,6 +93,7 @@
 # Test stage naming with render target handle.
 
 VK_OBJECT_TYPE_COMMAND_BUFFER = 6
+VK_OBJECT_TYPE_RENDER_PASS = 18
 VK_OBJECT_TYPE_FRAMEBUFFER = 24
 
 trace.add_gpu_render_stages(
@@ -106,9 +107,12 @@
     hw_queue_id=0,
     stage_id=0,
     context=42,
-    render_target_handle=0x10)
+    render_target_handle=0x10,
+    render_pass_handle=0x20,
+    command_buffer_handle=0x30)
 
-# adding a marker with non-FRAMEBUFFER should not affect the name.
+# adding a marker with COMMAND_BUFFER and RENDER_PASS should affect only the
+# relevant handles.
 trace.add_vk_debug_marker(
     ts=91,
     pid=100,
@@ -123,16 +127,17 @@
     hw_queue_id=0,
     stage_id=0,
     context=42,
-    render_target_handle=0x10)
+    render_target_handle=0x10,
+    render_pass_handle=0x10,
+    command_buffer_handle=0x10)
 
-# adding a marker with FRAMEBUFFER changes the name.
 trace.add_vk_debug_marker(
     ts=101,
     pid=100,
     vk_device=1,
-    obj_type=VK_OBJECT_TYPE_FRAMEBUFFER,
+    obj_type=VK_OBJECT_TYPE_RENDER_PASS,
     obj=0x10,
-    obj_name="framebuffer")
+    obj_name="render_pass")
 trace.add_gpu_render_stages(
     ts=110,
     event_id=10,
@@ -140,18 +145,39 @@
     hw_queue_id=0,
     stage_id=0,
     context=42,
-    render_target_handle=0x10)
+    render_target_handle=0x10,
+    render_pass_handle=0x10,
+    command_buffer_handle=0x10)
 
-# setting the name again replace the name
+# adding a marker with FRAMEBUFFER changes the name of the stage as well.
 trace.add_vk_debug_marker(
     ts=111,
     pid=100,
     vk_device=1,
     obj_type=VK_OBJECT_TYPE_FRAMEBUFFER,
     obj=0x10,
-    obj_name="renamed_buffer")
+    obj_name="framebuffer")
 trace.add_gpu_render_stages(
     ts=120,
+    event_id=10,
+    duration=5,
+    hw_queue_id=0,
+    stage_id=0,
+    context=42,
+    render_target_handle=0x10,
+    render_pass_handle=0x10,
+    command_buffer_handle=0x10)
+
+# setting the name again replace the name
+trace.add_vk_debug_marker(
+    ts=121,
+    pid=100,
+    vk_device=1,
+    obj_type=VK_OBJECT_TYPE_FRAMEBUFFER,
+    obj=0x10,
+    obj_name="renamed_buffer")
+trace.add_gpu_render_stages(
+    ts=130,
     event_id=11,
     duration=5,
     hw_queue_id=0,
@@ -161,6 +187,6 @@
 
 # Check that a hw_queue_id=-1 doesn't blow up.
 trace.add_gpu_render_stages(
-    ts=120, event_id=12, duration=5, hw_queue_id=-1, stage_id=-1, context=42)
+    ts=140, event_id=12, duration=5, hw_queue_id=-1, stage_id=-1, context=42)
 
 print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/gpu_render_stages.sql b/test/trace_processor/gpu_render_stages.sql
index dbfe545..c85d162 100644
--- a/test/trace_processor/gpu_render_stages.sql
+++ b/test/trace_processor/gpu_render_stages.sql
@@ -15,9 +15,10 @@
 --
 SELECT track.name AS track_name, gpu_track.description AS track_desc, ts, dur,
     gpu_slice.name AS slice_name, depth, flat_key, string_value,
-    gpu_slice.context_id, render_target, submission_id, hw_queue_id
+    gpu_slice.context_id, render_target, render_target_name, render_pass, render_pass_name,
+    command_buffer, command_buffer_name, submission_id, hw_queue_id
 FROM gpu_track
 LEFT JOIN track USING (id)
-INNER JOIN gpu_slice on gpu_track.id=gpu_slice.track_id
+INNER JOIN gpu_slice ON gpu_track.id=gpu_slice.track_id
 LEFT JOIN args ON gpu_slice.arg_set_id = args.arg_set_id
 ORDER BY ts;
diff --git a/test/trace_processor/graphics_frame_events.out b/test/trace_processor/graphics_frame_events.out
index 76e4e13..fd7c42a 100644
--- a/test/trace_processor/graphics_frame_events.out
+++ b/test/trace_processor/graphics_frame_events.out
@@ -16,5 +16,5 @@
 8,"layer3[buffer:2]",13,"Post",12,"layer_name","layer3"
 9,"layer3[buffer:3]",5,"Attach",15,"layer_name","layer3"
 10,"layer3[buffer:3]",10,"Cancel",16,"layer_name","layer3"
-16,"Displayed Frame",-1,6,17,"[NULL]","[NULL]"
+16,"Displayed Frame",-1,"6",17,"[NULL]","[NULL]"
 16,"layer5[buffer:6]",0,"PresentFenceSignaled",17,"layer_name","layer5"
diff --git a/test/trace_processor/heap_graph.textproto b/test/trace_processor/heap_graph.textproto
index 295e284..b8407aa 100644
--- a/test/trace_processor/heap_graph.textproto
+++ b/test/trace_processor/heap_graph.textproto
@@ -47,6 +47,11 @@
       reference_field_id: 2
       reference_object_id: 0x01
     }
+    objects {
+      id: 0x05
+      type_id: 4
+      self_size: 256
+    }
     continued: true
     index: 1
   }
@@ -67,6 +72,10 @@
       iid: 3
       str: "a"
     }
+    type_names {
+      iid: 4
+      str: "a[]"
+    }
     field_names {
       iid: 1
       str: "FactoryProducerDelegateImplActor.foo"
diff --git a/test/trace_processor/heap_graph_flamegraph.sql b/test/trace_processor/heap_graph_flamegraph.sql
new file mode 100644
index 0000000..157095f
--- /dev/null
+++ b/test/trace_processor/heap_graph_flamegraph.sql
@@ -0,0 +1,12 @@
+SELECT
+  id,
+  depth,
+  name,
+  map_name,
+  count,
+  cumulative_count,
+  size,
+  cumulative_size,
+  parent_id
+FROM experimental_flamegraph(601908408518618, 1, 'graph')
+LIMIT 10
diff --git a/test/trace_processor/heap_graph_flamegraph_system-server-heap-graph.out b/test/trace_processor/heap_graph_flamegraph_system-server-heap-graph.out
new file mode 100644
index 0000000..8f822d1
--- /dev/null
+++ b/test/trace_processor/heap_graph_flamegraph_system-server-heap-graph.out
@@ -0,0 +1,11 @@
+"id","depth","name","map_name","count","cumulative_count","size","cumulative_size","parent_id"
+0,0,"java.lang.Class<boolean>","JAVA",2,4,240,276,"[NULL]"
+1,1,"java.lang.Object[]","JAVA",1,1,12,12,0
+2,1,"java.lang.String","JAVA",1,1,24,24,0
+3,0,"java.lang.Class<byte>","JAVA",3,4,360,384,"[NULL]"
+4,1,"java.lang.String","JAVA",1,1,24,24,3
+5,0,"java.lang.Class<short>","JAVA",2,3,240,264,"[NULL]"
+6,1,"java.lang.String","JAVA",1,1,24,24,5
+7,0,"java.lang.Class<char>","JAVA",2,3,240,264,"[NULL]"
+8,1,"java.lang.String","JAVA",1,1,24,24,7
+9,0,"java.lang.Class<int>","JAVA",2,3,240,264,"[NULL]"
diff --git a/test/trace_processor/heap_graph_object.out b/test/trace_processor/heap_graph_object.out
index a25b67b..1d3b6eb 100644
--- a/test/trace_processor/heap_graph_object.out
+++ b/test/trace_processor/heap_graph_object.out
@@ -3,3 +3,4 @@
 1,"heap_graph_object",2,10,2,32,-1,-1,1,1,"Foo","[NULL]","[NULL]"
 2,"heap_graph_object",2,10,3,128,-1,-1,1,0,"Foo","[NULL]","[NULL]"
 3,"heap_graph_object",2,10,4,256,-1,-1,1,0,"a","DeobfuscatedA","[NULL]"
+4,"heap_graph_object",2,10,5,256,-1,-1,2,0,"a[]","DeobfuscatedA[]","[NULL]"
diff --git a/test/trace_processor/heap_profile_flamegraph.sql b/test/trace_processor/heap_profile_flamegraph.sql
new file mode 100644
index 0000000..fa90b15
--- /dev/null
+++ b/test/trace_processor/heap_profile_flamegraph.sql
@@ -0,0 +1 @@
+select * from experimental_flamegraph(605908369259172, 1, 'native')  limit 10;
diff --git a/test/trace_processor/heap_profile_flamegraph_system-server-native-profile.out b/test/trace_processor/heap_profile_flamegraph_system-server-native-profile.out
new file mode 100644
index 0000000..42ab65d
--- /dev/null
+++ b/test/trace_processor/heap_profile_flamegraph_system-server-native-profile.out
@@ -0,0 +1,11 @@
+"id","type","depth","name","map_name","count","cumulative_count","size","cumulative_size","alloc_count","cumulative_alloc_count","alloc_size","cumulative_alloc_size","parent_id"
+0,"experimental_flamegraph_nodes",0,"__start_thread","/apex/com.android.runtime/lib64/bionic/libc.so",0,8,0,84848,0,210,0,1084996,"[NULL]"
+1,"experimental_flamegraph_nodes",1,"_ZL15__pthread_startPv","/apex/com.android.runtime/lib64/bionic/libc.so",0,8,0,84848,0,210,0,1084996,0
+2,"experimental_flamegraph_nodes",2,"_ZN7android14AndroidRuntime15javaThreadShellEPv","/system/lib64/libandroid_runtime.so",0,5,0,27704,0,77,0,348050,1
+3,"experimental_flamegraph_nodes",3,"_ZN7android6Thread11_threadLoopEPv","/system/lib64/libutils.so",0,5,0,27704,0,77,0,348050,2
+4,"experimental_flamegraph_nodes",4,"_ZN7android10PoolThread10threadLoopEv","/system/lib64/libbinder.so",0,1,0,4096,0,64,0,279182,3
+5,"experimental_flamegraph_nodes",5,"_ZN7android14IPCThreadState14joinThreadPoolEb","/system/lib64/libbinder.so",0,1,0,4096,0,64,0,279182,4
+6,"experimental_flamegraph_nodes",6,"_ZN7android14IPCThreadState20getAndExecuteCommandEv","/system/lib64/libbinder.so",0,1,0,4096,0,64,0,279182,5
+7,"experimental_flamegraph_nodes",7,"_ZN7android14IPCThreadState14executeCommandEi","/system/lib64/libbinder.so",0,1,0,4096,0,64,0,279182,6
+8,"experimental_flamegraph_nodes",8,"_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j","/system/lib64/libbinder.so",0,1,0,4096,0,64,0,279182,7
+9,"experimental_flamegraph_nodes",9,"_ZN11JavaBBinder10onTransactEjRKN7android6ParcelEPS1_j","/system/lib64/libandroid_runtime.so",0,0,0,0,0,60,0,262730,8
diff --git a/test/trace_processor/heap_profile_tracker_new_stack.out b/test/trace_processor/heap_profile_tracker_new_stack.out
new file mode 100644
index 0000000..205768d
--- /dev/null
+++ b/test/trace_processor/heap_profile_tracker_new_stack.out
@@ -0,0 +1,5 @@
+"id","type","ts","upid","callsite_id","count","size"
+0,"heap_profile_allocation",0,0,0,1,1
+1,"heap_profile_allocation",0,0,0,-1,-1
+2,"heap_profile_allocation",1,0,0,1,1
+3,"heap_profile_allocation",1,0,0,-1,-1
diff --git a/test/trace_processor/heap_profile_tracker_new_stack.sql b/test/trace_processor/heap_profile_tracker_new_stack.sql
new file mode 100644
index 0000000..efed7da
--- /dev/null
+++ b/test/trace_processor/heap_profile_tracker_new_stack.sql
@@ -0,0 +1 @@
+select * from heap_profile_allocation;
diff --git a/test/trace_processor/heap_profile_tracker_new_stack.textproto b/test/trace_processor/heap_profile_tracker_new_stack.textproto
new file mode 100644
index 0000000..f84c30a
--- /dev/null
+++ b/test/trace_processor/heap_profile_tracker_new_stack.textproto
@@ -0,0 +1,76 @@
+packet {
+  clock_snapshot {
+    clocks: {
+      clock_id: 6 # BOOTTIME
+      timestamp: 0
+    }
+    clocks: {
+      clock_id: 4 # MONOTONIC_COARSE
+      timestamp: 0
+    }
+  }
+}
+
+packet {
+  previous_packet_dropped: true
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  interned_data {
+    mappings {
+      iid: 1
+    }
+    frames {
+      iid: 1
+      mapping_id: 1
+      rel_pc: 0x123
+    }
+    callstacks {
+      iid: 1
+      frame_ids: 1
+    }
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  profile_packet {
+    index: 0
+    continued: false
+    process_dumps {
+      samples {
+        callstack_id: 1
+        self_allocated: 1
+        alloc_count: 1
+        self_freed: 1
+        free_count: 1
+      }
+    }
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 1
+  interned_data {
+    callstacks {
+      iid: 2
+      frame_ids: 1
+    }
+  }
+  profile_packet {
+    index: 1
+    continued: false
+    process_dumps {
+      timestamp: 1
+      samples {
+        callstack_id: 2
+        self_allocated: 1
+        alloc_count: 1
+        self_freed: 1
+        free_count: 1
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/index b/test/trace_processor/index
index e6d6286..f929928 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -57,6 +57,8 @@
 
 # Rss stats
 rss_stat_mm_id.py rss_stat.sql rss_stat_mm_id.out
+rss_stat_mm_id_clone.py rss_stat.sql rss_stat_mm_id_clone.out
+rss_stat_mm_id_reuse.py rss_stat.sql rss_stat_mm_id_reuse.out
 rss_stat_legacy.py rss_stat.sql rss_stat_legacy.out
 
 # Memory counters
@@ -129,6 +131,7 @@
 # GPU trace tests.
 gpu_counters.py gpu_counters.sql gpu_counters.out
 gpu_render_stages.py gpu_render_stages.sql gpu_render_stages.out
+vulkan_api_events.py vulkan_api_events.sql vulkan_api_events.out
 gpu_log.py gpu_log.sql gpu_log.out
 
 # Clock sync
@@ -151,6 +154,11 @@
 heap_graph.textproto heap_graph_reference.sql heap_graph_reference.out
 heap_graph_interleaved.textproto heap_graph_object.sql heap_graph_interleaved_object.out
 heap_graph_interleaved.textproto heap_graph_reference.sql heap_graph_interleaved_reference.out
+../data/system-server-heap-graph.pftrace heap_graph_flamegraph.sql heap_graph_flamegraph_system-server-heap-graph.out
+../data/system-server-native-profile heap_profile_flamegraph.sql heap_profile_flamegraph_system-server-native-profile.out
+heap_profile_tracker_new_stack.textproto heap_profile_tracker_new_stack.sql heap_profile_tracker_new_stack.out
+
+stack_profile_tracker_empty_callstack.textproto stack_profile_tracker_empty_callstack.sql stack_profile_tracker_empty_callstack.out
 
 # TrackEvent tests.
 track_event_same_tids.textproto process_tracking.sql track_event_same_tids_threads.out
@@ -172,3 +180,6 @@
 
 # Ensures process -> package matching works as expected.
 process_metadata_matching.textproto process_metadata_matching.sql process_metadata_matching.out
+
+# Trace size
+../data/android_sched_and_ps.pb trace_size.sql android_sched_and_ps_trace_size.out
diff --git a/test/trace_processor/metadata.sql b/test/trace_processor/metadata.sql
index 46f9e6b..3943251 100644
--- a/test/trace_processor/metadata.sql
+++ b/test/trace_processor/metadata.sql
@@ -13,4 +13,4 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-select name, str_value from metadata order by name;
+select name, str_value from metadata where str_value is not null order by name;
diff --git a/test/trace_processor/rss_stat_mm_id copy.py b/test/trace_processor/rss_stat_mm_id copy.py
new file mode 100644
index 0000000..d2b5e5a
--- /dev/null
+++ b/test/trace_processor/rss_stat_mm_id copy.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+# Copyright (C) 2019 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.
+
+# This synthetic trace tests handling of the mm_id field in the rss_stat
+# event.
+
+from os import sys, path
+
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+import synth_common
+
+trace = synth_common.create_trace()
+
+trace.add_process_tree_packet(ts=1)
+trace.add_process(10, 0, "process")
+
+trace.add_ftrace_packet(0)
+
+# Create a new child process (treated internally as a thread) of kthreadd.
+trace.add_newtask(ts=50, tid=2, new_tid=3, new_comm="kthread_child", flags=0)
+
+# Add an event on tid 3 which affects its own rss.
+trace.add_rss_stat(ts=90, tid=3, member=0, size=9, mm_id=4321, curr=True)
+
+# Try to add an event for tid 10. However, as we've not seen an event
+# with curr == True for tid == 10, this event will be dropped.
+trace.add_rss_stat(ts=91, tid=3, member=0, size=900, mm_id=1234, curr=False)
+
+# Add an event for tid 3 from tid 10. This emulates e.g. direct reclaim
+# where a process reaches into another process' mm struct.
+trace.add_rss_stat(ts=99, tid=10, member=0, size=10, mm_id=4321, curr=False)
+
+# Add an event on tid 10 which affects its own rss.
+trace.add_rss_stat(ts=100, tid=10, member=0, size=1000, mm_id=1234, curr=True)
+
+# Add an event on tid 10 from tid 3. This emlates e.g. background reclaim
+# where kthreadd is cleaning up the mm struct of another process.
+trace.add_rss_stat(ts=101, tid=3, member=0, size=900, mm_id=1234, curr=False)
+
+print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/rss_stat_mm_id_clone.out b/test/trace_processor/rss_stat_mm_id_clone.out
new file mode 100644
index 0000000..0969481
--- /dev/null
+++ b/test/trace_processor/rss_stat_mm_id_clone.out
@@ -0,0 +1,9 @@
+"ts","name","pid","name","value"
+100,"mem.rss.file",10,"parent_process",100.000000
+100,"mem.rss.file",2,"kthreadd",10.000000
+102,"mem.rss.file",2,"kthreadd",20.000000
+102,"mem.rss.file",11,"child_process",90.000000
+104,"mem.rss.file",11,"child_process",10.000000
+105,"mem.rss.file",10,"parent_process",95.000000
+107,"mem.rss.file",10,"parent_process",105.000000
+108,"mem.rss.file",10,"parent_process",110.000000
diff --git a/test/trace_processor/rss_stat_mm_id_clone.py b/test/trace_processor/rss_stat_mm_id_clone.py
new file mode 100644
index 0000000..308b6b4
--- /dev/null
+++ b/test/trace_processor/rss_stat_mm_id_clone.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+# Copyright (C) 2019 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.
+
+# This synthetic trace tests handling of the mm_id field in the rss_stat
+# event during clone events which have various flag combinations set.
+
+from os import sys, path
+
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+import synth_common
+
+trace = synth_common.create_trace()
+
+trace.add_process_tree_packet(ts=1)
+trace.add_process(10, 1, "parent_process")
+trace.add_process(3, 2, "kernel_thread")
+
+# In this packet, check what happens to userspace processes with different
+# clone flags.
+trace.add_ftrace_packet(1)
+
+# Emit an rss stat event for the main thread of the process to associate it
+# with an mm_id.
+trace.add_rss_stat(100, tid=10, member=0, size=100, mm_id=0x1234, curr=1)
+
+# Create a newtask event emulating vfork/posix_spawn (i.e. CLONE_VM and
+# CLONE_VFORK set).
+trace.add_newtask(
+    101,
+    tid=10,
+    new_tid=11,
+    new_comm="child_process",
+    flags=synth_common.CLONE_VFORK | synth_common.CLONE_VM)
+
+# The child process will now change its own (and parent's) VM space with
+# |curr| set to 1 (emulating cleaning up some memory in parent).
+trace.add_rss_stat(102, tid=11, member=0, size=90, mm_id=0x1234, curr=1)
+
+# At this point, the child process will obtain a new mm struct. From this
+# point on, all mm_ids from the child should be different from the parent.
+
+# The child process will now change its parents VM space with curr set to
+# 0 (emulating e.g. cleaning up its stack).
+trace.add_rss_stat(103, tid=11, member=0, size=85, mm_id=0x1234, curr=0)
+
+# Now the child process should exec another process.
+
+# The child can now change its own memory.
+trace.add_rss_stat(104, tid=11, member=0, size=10, mm_id=0x5678, curr=1)
+
+# The parent can now resume execution and may emit another rss event.
+trace.add_rss_stat(105, tid=10, member=0, size=95, mm_id=0x1234, curr=1)
+
+# The parent can now go ahead and start a new thread.
+trace.add_newtask(
+    106,
+    tid=10,
+    new_tid=12,
+    new_comm="parent_thread",
+    flags=synth_common.CLONE_VM | synth_common.CLONE_THREAD)
+
+# Since this thread shares mm space with the parent, it should have the
+# same mm id and have curr set to 1.
+trace.add_rss_stat(107, tid=12, member=0, size=105, mm_id=0x1234, curr=1)
+
+# The parent can also emit events with the same mm struct at the same time.
+trace.add_rss_stat(108, tid=10, member=0, size=110, mm_id=0x1234, curr=1)
+
+# In this packet, we check what happens to kernel threads in RSS stat.
+trace.add_ftrace_packet(1)
+
+# Emit an rss stat event for the the existing kernel thread.
+trace.add_rss_stat(100, tid=3, member=0, size=10, mm_id=0x2345, curr=1)
+
+# Start a new kernel thread.
+trace.add_newtask(
+    101,
+    tid=2,
+    new_tid=4,
+    new_comm="kernel_thread2",
+    flags=synth_common.CLONE_VM)
+
+# Emit a rss stat for the new kernel thread.
+trace.add_rss_stat(102, tid=4, member=0, size=20, mm_id=0x2345, curr=1)
+
+print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/rss_stat_mm_id_reuse.out b/test/trace_processor/rss_stat_mm_id_reuse.out
new file mode 100644
index 0000000..396f5f7
--- /dev/null
+++ b/test/trace_processor/rss_stat_mm_id_reuse.out
@@ -0,0 +1,3 @@
+"ts","name","pid","name","value"
+100,"mem.rss.file",10,"parent_process",100.000000
+103,"mem.rss.file",10,"new_process",10.000000
diff --git a/test/trace_processor/rss_stat_mm_id_reuse.py b/test/trace_processor/rss_stat_mm_id_reuse.py
new file mode 100644
index 0000000..58d7b4d
--- /dev/null
+++ b/test/trace_processor/rss_stat_mm_id_reuse.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+# Copyright (C) 2019 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.
+
+# This synthetic trace tests handling of the mm_id field in the rss_stat
+# event when mm_structs are reused on process death.
+
+from os import sys, path
+
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+import synth_common
+
+trace = synth_common.create_trace()
+
+trace.add_process_tree_packet(ts=1)
+trace.add_process(10, 1, "parent_process")
+
+trace.add_ftrace_packet(1)
+
+# Emit an event for the process.
+trace.add_rss_stat(100, tid=10, member=0, size=100, mm_id=0x1234, curr=1)
+
+# Now kill the process.
+trace.add_process_free(ts=101, tid=10, comm="parent_process", prio=0)
+
+# Create a new thread which reuses the pid and mm struct.
+trace.add_newtask(102, tid=1, new_tid=10, new_comm="new_process", flags=0)
+
+# Emit an event for the new thread.
+trace.add_rss_stat(103, tid=10, member=0, size=10, mm_id=0x1234, curr=1)
+
+print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/stack_profile_tracker_empty_callstack.out b/test/trace_processor/stack_profile_tracker_empty_callstack.out
new file mode 100644
index 0000000..eb69505
--- /dev/null
+++ b/test/trace_processor/stack_profile_tracker_empty_callstack.out
@@ -0,0 +1,2 @@
+"count"
+0
diff --git a/test/trace_processor/stack_profile_tracker_empty_callstack.sql b/test/trace_processor/stack_profile_tracker_empty_callstack.sql
new file mode 100644
index 0000000..c61df0d
--- /dev/null
+++ b/test/trace_processor/stack_profile_tracker_empty_callstack.sql
@@ -0,0 +1 @@
+select count(1) as count from heap_profile_allocation;
diff --git a/test/trace_processor/stack_profile_tracker_empty_callstack.textproto b/test/trace_processor/stack_profile_tracker_empty_callstack.textproto
new file mode 100644
index 0000000..c0b6dfe
--- /dev/null
+++ b/test/trace_processor/stack_profile_tracker_empty_callstack.textproto
@@ -0,0 +1,45 @@
+packet {
+  clock_snapshot {
+    clocks: {
+      clock_id: 6 # BOOTTIME
+      timestamp: 0
+    }
+    clocks: {
+      clock_id: 4 # MONOTONIC_COARSE
+      timestamp: 0
+    }
+  }
+}
+
+packet {
+  previous_packet_dropped: true
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  interned_data {
+    callstacks {
+      iid: 1
+    }
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  profile_packet {
+    index: 0
+    continued: false
+    process_dumps {
+      samples {
+        callstack_id: 1
+        self_allocated: 1
+        alloc_count: 1
+      }
+      samples {
+        callstack_id: 1
+        self_allocated: 1
+        alloc_count: 1
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/trace_size.sql b/test/trace_processor/trace_size.sql
new file mode 100644
index 0000000..01704a2
--- /dev/null
+++ b/test/trace_processor/trace_size.sql
@@ -0,0 +1,16 @@
+--
+-- Copyright 2020 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
+--
+--     https://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.
+--
+select int_value from metadata where name = 'trace_size_bytes';
diff --git a/test/trace_processor/track_event_args.sql b/test/trace_processor/track_event_args.sql
index 7b7b978..10187a2 100644
--- a/test/trace_processor/track_event_args.sql
+++ b/test/trace_processor/track_event_args.sql
@@ -13,4 +13,4 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-select arg_set_id, flat_key, key, int_value, string_value from args where arg_set_id >= 2 order by arg_set_id, key asc;
\ No newline at end of file
+select arg_set_id, flat_key, key, int_value, string_value from args order by arg_set_id, key asc;
\ No newline at end of file
diff --git a/test/trace_processor/track_event_same_tids_slices.out b/test/trace_processor/track_event_same_tids_slices.out
index 48fa77b..d882f13 100644
--- a/test/trace_processor/track_event_same_tids_slices.out
+++ b/test/trace_processor/track_event_same_tids_slices.out
@@ -1,3 +1,3 @@
-"thread","ts","dur","category","name","arg_set_id"
-"t1",1000,0,"cat","name1",0
-"t2",2000,0,"cat","name2",0
+"track","process","thread","thread_process","ts","dur","category","name","arg_set_id"
+"[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","name1",0
+"[NULL]","[NULL]","t2","[NULL]",2000,0,"cat","name2",0
diff --git a/test/trace_processor/track_event_slices.sql b/test/trace_processor/track_event_slices.sql
index 42f7b69..5146810 100644
--- a/test/trace_processor/track_event_slices.sql
+++ b/test/trace_processor/track_event_slices.sql
@@ -14,13 +14,20 @@
 -- limitations under the License.
 --
 select
+  track.name as track,
+  process.name as process,
   thread.name as thread,
+  thread_process.name as thread_process,
   slice.ts,
   slice.dur,
   slice.category,
   slice.name,
   slice.arg_set_id
 from slice
+left join track on slice.track_id = track.id
+left join process_track on slice.track_id = process_track.id
+left join process on process_track.upid = process.upid
 left join thread_track on slice.track_id = thread_track.id
 left join thread on thread_track.utid = thread.utid
+left join process thread_process on thread.upid = thread_process.upid
 order by ts asc;
diff --git a/test/trace_processor/track_event_tracks.textproto b/test/trace_processor/track_event_tracks.textproto
index fd8cdc8..b76fa36 100644
--- a/test/trace_processor/track_event_tracks.textproto
+++ b/test/trace_processor/track_event_tracks.textproto
@@ -5,6 +5,7 @@
   incremental_state_cleared: true
   track_descriptor {
     uuid: 1
+    parent_uuid: 10
     thread {
       pid: 5
       tid: 1
@@ -24,6 +25,7 @@
   incremental_state_cleared: true
   track_descriptor {
     uuid: 2
+    parent_uuid: 10
     thread {
       pid: 5
       tid: 2
@@ -36,13 +38,54 @@
     }
   }
 }
+# Both thread tracks are nested underneath this process track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  track_descriptor {
+    uuid: 10
+    process {
+      pid: 5
+      process_name: "p1"
+    }
+  }
+}
+# And we have an async track underneath the process too.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  track_descriptor {
+    uuid: 11
+    parent_uuid: 10
+    name: "async"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 100
+  track_descriptor {
+    uuid: 12
+    parent_uuid: 10
+    name: "async2"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 200
+  track_descriptor {
+    uuid: 12
+    parent_uuid: 10
+    name: "async2"
+  }
+}
+
 # Should appear on default track "t1".
 packet {
   trusted_packet_sequence_id: 1
   timestamp: 1000
   track_event {
     categories: "cat"
-    name: "name1"
+    name: "event1_on_t1"
     type: 3
   }
 }
@@ -52,7 +95,7 @@
   timestamp: 2000
   track_event {
     categories: "cat"
-    name: "name2"
+    name: "event1_on_t2"
     type: 3
   }
 }
@@ -63,7 +106,173 @@
   track_event {
     track_uuid: 2
     categories: "cat"
-    name: "name3"
+    name: "event2_on_t2"
     type: 3
   }
 }
+# Should appear on process track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 4000
+  track_event {
+    track_uuid: 10
+    categories: "cat"
+    name: "event1_on_p1"
+    type: 3
+  }
+}
+# Should appear on async track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 5000
+  track_event {
+    track_uuid: 11
+    categories: "cat"
+    name: "event1_on_async"
+    type: 3
+  }
+}
+# Event for the "async2" track starting on one thread and ending on another.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 5100
+  track_event {
+    track_uuid: 12
+    categories: "cat"
+    name: "event1_on_async2"
+    type: 1
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 5200
+  track_event {
+    track_uuid: 12
+    categories: "cat"
+    name: "event1_on_async2"
+    type: 2
+  }
+}
+
+# If we later see another track descriptor for tid 1, but with a different uuid,
+# we should detect tid reuse and start a new thread.
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 10000
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 3
+    parent_uuid: 10
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t3"
+    }
+  }
+}
+# Should appear on t3.
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 11000
+  track_event {
+    track_uuid: 3
+    categories: "cat"
+    name: "event1_on_t3"
+    type: 3
+  }
+}
+
+# If we later see another track descriptor for pid 5, but with a different uuid,
+# we should detect pid reuse and start a new process.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 20000
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 20
+    process {
+      pid: 5
+      process_name: "p2"
+    }
+  }
+}
+# Should appear on p2.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 21000
+  track_event {
+    track_uuid: 20
+    categories: "cat"
+    name: "event1_on_p2"
+    type: 3
+  }
+}
+# Another thread t4 in the new process.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 22000
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 21
+    parent_uuid: 20
+    thread {
+      pid: 5
+      tid: 4
+      thread_name: "t4"
+    }
+  }
+}
+# Should appear on t4.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 22000
+  track_event {
+    track_uuid: 21
+    categories: "cat"
+    name: "event1_on_t4"
+    type: 3
+  }
+}
+
+# Another packet for a thread track in the old process, badly sorted.
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 6000
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    name: "event3_on_t1"
+    type: 3
+  }
+}
+
+# Override the track to the default descriptor track for an event with a
+# TrackEvent type. Should appear on the default descriptor track instead of
+# "t1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 30000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "event1_on_t1"
+    type: 3
+  }
+}
+
+# But a legacy event without TrackEvent type falls back to legacy tracks (based
+# on ThreadDescriptor / async IDs / legacy instant scopes). This instant event
+# should appear on the process track "p2".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 31000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "event1_on_t1"
+    legacy_event {
+      phase: 73               # 'I'
+      instant_event_scope: 2  # Process scope
+    }
+  }
+}
diff --git a/test/trace_processor/track_event_tracks_slices.out b/test/trace_processor/track_event_tracks_slices.out
index 93c7186..d7119df 100644
--- a/test/trace_processor/track_event_tracks_slices.out
+++ b/test/trace_processor/track_event_tracks_slices.out
@@ -1,4 +1,13 @@
-"thread","ts","dur","category","name","arg_set_id"
-"t1",1000,0,"cat","name1",0
-"t2",2000,0,"cat","name2",0
-"t2",3000,0,"cat","name3",0
+"track","process","thread","thread_process","ts","dur","category","name","arg_set_id"
+"[NULL]","[NULL]","t1","p1",1000,0,"cat","event1_on_t1",0
+"[NULL]","[NULL]","t2","p1",2000,0,"cat","event1_on_t2",0
+"[NULL]","[NULL]","t2","p1",3000,0,"cat","event2_on_t2",0
+"[NULL]","p1","[NULL]","[NULL]",4000,0,"cat","event1_on_p1",8
+"async","p1","[NULL]","[NULL]",5000,0,"cat","event1_on_async",8
+"async2","p1","[NULL]","[NULL]",5100,100,"cat","event1_on_async2",3
+"[NULL]","[NULL]","t1","p1",6000,0,"cat","event3_on_t1",0
+"[NULL]","[NULL]","t3","p1",11000,0,"cat","event1_on_t3",0
+"[NULL]","p2","[NULL]","[NULL]",21000,0,"cat","event1_on_p2",7
+"[NULL]","[NULL]","t4","p2",22000,0,"cat","event1_on_t4",0
+"Default Track","[NULL]","[NULL]","[NULL]",30000,0,"cat","event1_on_t1",0
+"[NULL]","p2","[NULL]","[NULL]",31000,0,"cat","event1_on_t1",6
diff --git a/test/trace_processor/track_event_typed_args_args.out b/test/trace_processor/track_event_typed_args_args.out
index f08c63e..c66424f 100644
--- a/test/trace_processor/track_event_typed_args_args.out
+++ b/test/trace_processor/track_event_typed_args_args.out
@@ -1,5 +1,5 @@
 "arg_set_id","flat_key","key","int_value","string_value"
-2,"user_event.action","user_event.action","[NULL]","NewTab"
-3,"legacy_ipc.class","legacy_ipc.class","[NULL]","AUTOMATION"
-3,"legacy_ipc.line","legacy_ipc.line",10,"[NULL]"
-4,"keyed_service.name","keyed_service.name","[NULL]","MediaRouter"
+1,"user_event.action","user_event.action","[NULL]","NewTab"
+2,"legacy_ipc.class","legacy_ipc.class","[NULL]","AUTOMATION"
+2,"legacy_ipc.line","legacy_ipc.line",10,"[NULL]"
+3,"keyed_service.name","keyed_service.name","[NULL]","MediaRouter"
diff --git a/test/trace_processor/track_event_typed_args_slices.out b/test/trace_processor/track_event_typed_args_slices.out
index 6a9a969..830accb 100644
--- a/test/trace_processor/track_event_typed_args_slices.out
+++ b/test/trace_processor/track_event_typed_args_slices.out
@@ -1,4 +1,4 @@
-"thread","ts","dur","category","name","arg_set_id"
-"t1",1000,0,"cat","name1",2
-"t1",2000,0,"cat","name2",3
-"t1",3000,0,"cat","name3",4
+"track","process","thread","thread_process","ts","dur","category","name","arg_set_id"
+"[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","name1",1
+"[NULL]","[NULL]","t1","[NULL]",2000,0,"cat","name2",2
+"[NULL]","[NULL]","t1","[NULL]",3000,0,"cat","name3",3
diff --git a/test/trace_processor/vulkan_api_events.out b/test/trace_processor/vulkan_api_events.out
new file mode 100644
index 0000000..de409f9
--- /dev/null
+++ b/test/trace_processor/vulkan_api_events.out
@@ -0,0 +1,5 @@
+"track_name","track_desc","ts","dur","slice_name","depth","flat_key","int_value","context_id","command_buffer","submission_id"
+"Vulkan Events","[NULL]",10,2,"vkQueueSubmit",0,"pid",42,"[NULL]",100,1
+"Vulkan Events","[NULL]",10,2,"vkQueueSubmit",0,"tid",43,"[NULL]",100,1
+"Vulkan Events","[NULL]",20,2,"vkQueueSubmit",0,"pid",44,"[NULL]",200,2
+"Vulkan Events","[NULL]",20,2,"vkQueueSubmit",0,"tid",45,"[NULL]",200,2
diff --git a/test/trace_processor/vulkan_api_events.py b/test/trace_processor/vulkan_api_events.py
new file mode 100644
index 0000000..b279f7a
--- /dev/null
+++ b/test/trace_processor/vulkan_api_events.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+# Copyright (C) 2020 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.
+
+from os import sys, path
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+import synth_common
+
+trace = synth_common.create_trace()
+
+trace.add_vk_queue_submit(
+    ts=10,
+    dur=2,
+    pid=42,
+    tid=43,
+    vk_queue=10,
+    vk_command_buffers=[100],
+    submission_id=1)
+
+trace.add_vk_queue_submit(
+    ts=20,
+    dur=2,
+    pid=44,
+    tid=45,
+    vk_queue=11,
+    vk_command_buffers=[200, 300, 400],
+    submission_id=2)
+
+print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/vulkan_api_events.sql b/test/trace_processor/vulkan_api_events.sql
new file mode 100644
index 0000000..89fccd9
--- /dev/null
+++ b/test/trace_processor/vulkan_api_events.sql
@@ -0,0 +1,23 @@
+--
+-- Copyright 2020 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
+--
+--     https://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.
+--
+SELECT track.name AS track_name, gpu_track.description AS track_desc, ts, dur,
+    gpu_slice.name AS slice_name, depth, flat_key, int_value,
+    gpu_slice.context_id, command_buffer, submission_id
+FROM gpu_track
+LEFT JOIN track USING (id)
+INNER JOIN gpu_slice ON gpu_track.id=gpu_slice.track_id
+LEFT JOIN args ON gpu_slice.arg_set_id = args.arg_set_id
+ORDER BY ts;
diff --git a/tools/BUILD.gn b/tools/BUILD.gn
index 6214548..a018fd4 100644
--- a/tools/BUILD.gn
+++ b/tools/BUILD.gn
@@ -45,9 +45,7 @@
 
 if (is_linux && enable_perfetto_heapprofd) {
   executable("profiling_sample_distribution") {
-    sources = [
-      "profiling_sample_distribution.cc",
-    ]
+    sources = [ "profiling_sample_distribution.cc" ]
     deps = [
       "../gn:default_deps",
       "../src/base",
@@ -57,12 +55,8 @@
 }
 
 executable("idle_alloc") {
-  deps = [
-    "../gn:default_deps",
-  ]
-  sources = [
-    "idle_alloc.cc",
-  ]
+  deps = [ "../gn:default_deps" ]
+  sources = [ "idle_alloc.cc" ]
 }
 
 # The protoc binary can end up in out/protoc or out/gcc_like_host/protoc
@@ -73,22 +67,14 @@
 if (current_toolchain != host_toolchain) {
   copy("copy_protoc") {
     testonly = true
-    deps = [
-      protoc_target,
-    ]
+    deps = [ protoc_target ]
     host_out_dir = get_label_info(protoc_target, "root_out_dir")
-    sources = [
-      "$host_out_dir/protoc",
-    ]
-    outputs = [
-      "$root_build_dir/protoc",
-    ]
+    sources = [ "$host_out_dir/protoc" ]
+    outputs = [ "$root_build_dir/protoc" ]
   }
 } else {
   # Nothing to do, in this case protoc is already built in the root out dir.
   group("copy_protoc") {
-    public_deps = [
-      protoc_target,
-    ]
+    public_deps = [ protoc_target ]
   }
 }
diff --git a/tools/busy_threads/BUILD.gn b/tools/busy_threads/BUILD.gn
index 0f33776..84391fe 100644
--- a/tools/busy_threads/BUILD.gn
+++ b/tools/busy_threads/BUILD.gn
@@ -18,7 +18,5 @@
     "../../gn:default_deps",
     "../../src/base",
   ]
-  sources = [
-    "busy_threads.cc",
-  ]
+  sources = [ "busy_threads.cc" ]
 }
diff --git a/tools/compact_reencode/BUILD.gn b/tools/compact_reencode/BUILD.gn
index 6556b9d..799f313 100644
--- a/tools/compact_reencode/BUILD.gn
+++ b/tools/compact_reencode/BUILD.gn
@@ -31,7 +31,5 @@
     "../../protos/perfetto/trace/ftrace:zero",
     "../../src/base",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
 }
diff --git a/tools/cpu_utilization/BUILD.gn b/tools/cpu_utilization/BUILD.gn
index 1b70388..ccbdd2f 100644
--- a/tools/cpu_utilization/BUILD.gn
+++ b/tools/cpu_utilization/BUILD.gn
@@ -18,7 +18,5 @@
     "../../gn:default_deps",
     "../../src/base",
   ]
-  sources = [
-    "cpu_utilization.cc",
-  ]
+  sources = [ "cpu_utilization.cc" ]
 }
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index 2d92f39..cb1ba78 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -295,7 +295,7 @@
   elif args.test_type == 'metrics':
     index = os.path.join(test_dir, 'metrics', 'index')
   else:
-    print('Unknown test type {}. Supported: queries, metircs'.format(
+    print('Unknown test type {}. Supported: queries, metrics'.format(
         args.test_type))
     return 1
 
diff --git a/tools/dump_ftrace_stats/BUILD.gn b/tools/dump_ftrace_stats/BUILD.gn
index 3a047d5..1ee0cc0 100644
--- a/tools/dump_ftrace_stats/BUILD.gn
+++ b/tools/dump_ftrace_stats/BUILD.gn
@@ -19,7 +19,5 @@
     "../../include/perfetto/base",
     "../../src/base",
   ]
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
 }
diff --git a/tools/ftrace_proto_gen/BUILD.gn b/tools/ftrace_proto_gen/BUILD.gn
index a63d696..1b96c30 100644
--- a/tools/ftrace_proto_gen/BUILD.gn
+++ b/tools/ftrace_proto_gen/BUILD.gn
@@ -17,9 +17,7 @@
 
 perfetto_host_executable("ftrace_proto_gen") {
   testonly = true
-  sources = [
-    "main.cc",
-  ]
+  sources = [ "main.cc" ]
   deps = [
     ":lib",
     "../../gn:default_deps",
@@ -37,9 +35,7 @@
     "../../gn:gtest_and_gmock",
     "../../gn:protobuf_full",
   ]
-  sources = [
-    "ftrace_proto_gen_unittest.cc",
-  ]
+  sources = [ "ftrace_proto_gen_unittest.cc" ]
 }
 
 source_set("lib") {
diff --git a/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc b/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc
index 4339e47..9384622 100644
--- a/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc
+++ b/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc
@@ -47,7 +47,7 @@
  * limitations under the License.
  */
 
- )";
+)";
   *fout << "// Autogenerated by:\n";
   *fout << std::string("// ") + __FILE__ + "\n";
   *fout << "// Do not edit.\n";
diff --git a/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/tools/ftrace_proto_gen/ftrace_proto_gen.cc
index 4ef59b3..7ed8110 100644
--- a/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+++ b/tools/ftrace_proto_gen/ftrace_proto_gen.cc
@@ -49,7 +49,7 @@
  * limitations under the License.
  */
 
- )";
+)";
 
 }  // namespace
 
@@ -124,8 +124,7 @@
   *fout << std::string("// ") + __FILE__ + "\n";
   *fout << "// Do not edit.\n\n";
   *fout << R"(syntax = "proto2";)"
-        << "\n";
-  *fout << "option optimize_for = LITE_RUNTIME;\n\n";
+        << "\n\n";
 
   for (const std::string& group : groups) {
     *fout << R"(import "protos/perfetto/trace/ftrace/)" << group
@@ -267,7 +266,6 @@
 
   s += R"(
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 package perfetto.protos;
 
 )";
diff --git a/tools/gen_amalgamated b/tools/gen_amalgamated
index ea6e254..79a572d 100755
--- a/tools/gen_amalgamated
+++ b/tools/gen_amalgamated
@@ -117,6 +117,7 @@
 # ----------------------------------------------------------------------------
 
 tool_name = os.path.basename(__file__)
+project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
 preamble = """// Copyright (C) 2019 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -143,6 +144,12 @@
   return [item for item in items if re.match(whitelist, item)]
 
 
+def normalize_path(path):
+  path = os.path.relpath(path, project_root)
+  path = re.sub(r'^out/[^/]+/', '', path)
+  return path
+
+
 class Error(Exception):
   pass
 
@@ -332,7 +339,8 @@
         if not rel_path in allowed_files:
           return
         with open(full_path) as f:
-          self.source.append('// %s begin header: %s' % (tool_name, rel_path))
+          self.source.append(
+              '// %s begin header: %s' % (tool_name, normalize_path(full_path)))
           self.source.extend(
               self._process_source_includes(include_dirs, allowed_files, f))
         return
@@ -351,12 +359,13 @@
     if not os.path.exists(full_path):
       raise Error('Source file %s not found' % source_name)
     with open(full_path) as f:
-      self.source.append('// %s begin source: %s' % (tool_name, source_name))
+      self.source.append(
+          '// %s begin source: %s' % (tool_name, normalize_path(full_path)))
       try:
         self.source.extend(
             self._patch_source(
-                source_name,
-                self._process_source_includes(include_dirs, deps, f)))
+                source_name, self._process_source_includes(
+                    include_dirs, deps, f)))
       except Error as e:
         raise Error('Failed adding source %s: %s' % (source_name, e.message))
 
@@ -368,7 +377,8 @@
       full_path = os.path.join(gn_utils.repo_root(), include_dir, header_name)
       if os.path.exists(full_path):
         with open(full_path) as f:
-          self.header.append('// %s begin header: %s' % (tool_name, full_path))
+          self.header.append(
+              '// %s begin header: %s' % (tool_name, normalize_path(full_path)))
           self.header.extend(self._process_header_includes(include_dirs, f))
         return
     if self._compute_deps_only:
@@ -387,7 +397,8 @@
         return
       raise Error('Header file %s not found' % header_name)
     with open(full_path) as f:
-      self.header.append('// %s begin header: %s' % (tool_name, header_name))
+      self.header.append(
+          '// %s begin header: %s' % (tool_name, normalize_path(full_path)))
       try:
         self.header.extend(self._process_header_includes(include_dirs, f))
       except Error as e:
@@ -507,8 +518,8 @@
 Example build command:
 
 %s
-""" % (header_file, source_file, ' '.join(self.cflags), ' '.join(
-        self.ldflags), ' '.join(self.libs), ' '.join(build_cmd))
+""" % (header_file, source_file, ' '.join(self.cflags), ' '.join(self.ldflags),
+       ' '.join(self.libs), ' '.join(build_cmd))
 
   def get_build_command(self, output_prefix):
     """Returns an example command line for building the output source."""
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index ebc440a..7c1c444 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -62,6 +62,7 @@
     '//src/traced/probes:traced_probes',
     '//src/traced/service:traced',
     '//test/cts:perfetto_cts_deps',
+    '//test/cts:perfetto_cts_jni_deps',
 ]
 
 # Host targets
@@ -144,7 +145,11 @@
   with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
     lines = f.readlines()
   for line in (line.strip() for line in lines if not line.startswith('#')):
-    assert os.path.exists(line), line
+    assert os.path.exists(line), 'file %s should exist' % line
+    if line.startswith('test/data/'):
+        # Skip test data files that require GCS. They are only for benchmarks.
+        # We don't run benchmarks in the android tree.
+        continue
     if line.endswith('/'):
       yield line + '**/*'
     else:
@@ -166,9 +171,6 @@
         ('required', {'libperfetto_android_internal', 'trigger_perfetto'}),
     ],
     'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
-    'traced_perf': [
-        ('include_dirs', {'bionic/libc/kernel'}),
-    ],
     'trace_processor_shell': [
       ('dist', {'targets': ['sdk_repo']}),
       ('stl', 'libc++_static'),
@@ -226,6 +228,10 @@
     module.shared_libs.add('libz')
 
 
+def enable_uapi_headers(module):
+  module.include_dirs.add('bionic/libc/kernel')
+
+
 # Android equivalents for third-party libraries that the upstream project
 # depends on.
 builtin_deps = {
@@ -240,6 +246,7 @@
     '//gn:libunwindstack': enable_libunwindstack,
     '//gn:sqlite': enable_sqlite,
     '//gn:zlib': enable_zlib,
+    '//gn:bionic_kernel_uapi_headers' : enable_uapi_headers,
 }
 
 # ----------------------------------------------------------------------------
@@ -515,7 +522,7 @@
   if target.proto_plugin == 'proto':
     suffixes = ['pb']
     source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
-    cmd += ['--cpp_out=' + cpp_out_dir]
+    cmd += ['--cpp_out=lite=true:' + cpp_out_dir]
   elif target.proto_plugin == 'protozero':
     suffixes = ['pbzero']
     plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
diff --git a/tools/gen_bazel b/tools/gen_bazel
index f8150d3..00568b2 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -165,7 +165,10 @@
         continue
       res += '    %s = ' % k
       if isinstance(v, basestring):
-        res += '"%s",\n' % v
+        if v.startswith('PERFETTO_CONFIG.'):
+          res += '%s,\n' % v
+        else:
+          res += '"%s",\n' % v
       elif isinstance(v, bool):
         res += '%s,\n' % v
       elif isinstance(v, list):
@@ -198,7 +201,7 @@
 
 
 # Public visibility for targets in Bazel.
-PUBLIC_VISIBILITY = ['//visibility:public']
+PUBLIC_VISIBILITY = 'PERFETTO_CONFIG.public_visibility'
 
 
 def get_bazel_label_name(gn_name):
diff --git a/tools/gen_binary_descriptors b/tools/gen_binary_descriptors
index 439d596..839d7c0 100755
--- a/tools/gen_binary_descriptors
+++ b/tools/gen_binary_descriptors
@@ -37,6 +37,8 @@
         'src/protozero/test/example_proto/test_messages.descriptor.h',
     'protos/perfetto/trace/track_event/track_event.proto':
         'src/trace_processor/importers/proto/track_event.descriptor.h',
+    'protos/perfetto/metrics/custom_options.proto':
+        'src/trace_processor/metrics/custom_options.descriptor.h',
 }
 
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
@@ -83,6 +85,8 @@
         protoc_path,
         '--include_imports',
         '--proto_path=.',
+        '--proto_path=' + \
+            os.path.join(ROOT_DIR, "buildtools", "protobuf", "src"),
         '--descriptor_set_out={}'.format(fdescriptor.name),
         source,
     ],
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index 5cfece7..a8f9aa6 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -218,12 +218,12 @@
 
 
 def main():
-  config_result = merge_protos(COMMON_PROTOS + CONFIG_PROTOS,
-                               MERGED_CONFIG_PROTO, False)
-  trace_result = merge_protos(COMMON_PROTOS + TRACE_PROTOS + CONFIG_PROTOS,
-                              MERGED_TRACE_PROTO, False)
-  trace_result = merge_protos(METRICS_PROTOS, MERGED_METRICS_PROTO, True)
-  return 0 if config_result and trace_result else 1
+  result = merge_protos(COMMON_PROTOS + CONFIG_PROTOS, MERGED_CONFIG_PROTO,
+                        False)
+  result &= merge_protos(COMMON_PROTOS + TRACE_PROTOS + CONFIG_PROTOS,
+                         MERGED_TRACE_PROTO, False)
+  result &= merge_protos(METRICS_PROTOS, MERGED_METRICS_PROTO, True)
+  return 0 if result else 1
 
 
 if __name__ == '__main__':
diff --git a/tools/heap_profile b/tools/heap_profile
index eb5035f..8662bbf 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -31,8 +31,8 @@
 import urllib
 
 TRACE_TO_TEXT_SHAS = {
-    'linux': 'cd6d89e0ada48c0d7850206d80c324b912a3db42',
-    'mac': 'aadf14ce5988e5d5e283fb816f72bb2bba8cc9e4',
+    'linux': 'f9c206e242c49ebd4ec5e4138c61f4fb2816065b',
+    'mac': '7e1803f5c51d0763061d2980fd9af5750ac9d9c8',
 }
 TRACE_TO_TEXT_PATH = tempfile.gettempdir()
 TRACE_TO_TEXT_BASE_URL = ('https://storage.googleapis.com/perfetto/')
@@ -99,8 +99,11 @@
 duration_ms: {duration}
 write_into_file: true
 flush_timeout_ms: 30000
+flush_period_ms: 604800000
 '''
 
+# flush_period_ms of 1 week to suppress trace_processor_shell warning.
+
 CONTINUOUS_DUMP = """
       continuous_dump_config {{
         dump_phase_ms: 0
@@ -119,6 +122,13 @@
   IS_INTERRUPTED = True
 
 
+def print_no_profile_error():
+  print("No profiles generated", file=sys.stderr)
+  print(
+    "If this is unexpected, check "
+    "https://docs.perfetto.dev/#/heapprofd?id=troubleshooting.",
+    file=sys.stderr)
+
 def main(argv):
   parser = argparse.ArgumentParser()
   parser.add_argument(
@@ -344,6 +354,7 @@
   old_handler = signal.signal(signal.SIGINT, sigint_handler)
   print("Profiling active. Press Ctrl+C to terminate.")
   print("You may disconnect your device.")
+  print()
   exists = True
   device_connected = True
   while not device_connected or (exists and not IS_INTERRUPTED):
@@ -385,16 +396,12 @@
     if 'heap_profile-' in word:
       profile_path = word
   if profile_path is None:
-    print("Could not find trace_to_text output path.", file=sys.stderr)
+    print_no_profile_error();
     return 1
 
   profile_files = os.listdir(profile_path)
   if not profile_files:
-    print("No profiles generated", file=sys.stderr)
-    print(
-        "If this is unexpected, check "
-        "https://docs.perfetto.dev/#/heapprofd?id=troubleshooting.",
-        file=sys.stderr)
+    print_no_profile_error();
     return 1
 
   subprocess.check_call(
@@ -417,7 +424,8 @@
               env=os.environ,
               stdout=fd)
           if ret != 0:
-              print("Failed to symbolize.", file=sys.stderr)
+              print("Failed to symbolize. Continuing without symbols.",
+                    file=sys.stderr)
 
   print("Wrote profiles to {} (symlink {})".format(profile_path, symlink_path))
   print("These can be viewed using pprof. Googlers: head to pprof/ and "
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 43545f6..e1e9c20 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -44,11 +44,11 @@
 BUILD_DEPS_HOST = [
     # GN
     ('buildtools/mac/gn',
-     'https://storage.googleapis.com/perfetto/gn-mac-b5b65ca39d93a7cde9fa713be31b114755252f28',
-     'b5b65ca39d93a7cde9fa713be31b114755252f28', 'darwin'),
+     'https://storage.googleapis.com/perfetto/gn-mac-1695-83dad00a',
+     '4c0d45772aea4146699772165e8112fa76ceb295', 'darwin'),
     ('buildtools/linux64/gn',
-     'https://storage.googleapis.com/perfetto/gn-linux64-1370d9c5358868b7b66292821b6fe61950826870',
-     '1370d9c5358868b7b66292821b6fe61950826870', 'linux'),
+     'https://storage.googleapis.com/perfetto/gn-linux64-1695-83dad00a',
+     'fcabfc379bccaa65b4e2fc791594ba124dafc7d0', 'linux'),
 
     # clang-format
     ('buildtools/mac/clang-format',
@@ -84,7 +84,7 @@
     # lib from sources. Keep the SHA1s in sync with Chrome's src/buildtools/DEPS.
     ('buildtools/libcxx',
      'https://chromium.googlesource.com/chromium/llvm-project/libcxx.git',
-     '5938e0582bac570a41edb3d6a2217c299adc1bc6', 'all'),
+     '78d6a7767ed57b50122a161b91f59f19c9bd0d19', 'all'),
     ('buildtools/libcxxabi',
      'https://chromium.googlesource.com/chromium/llvm-project/libcxxabi.git',
      '0d529660e32d77d9111912d73f2c74fc5fa2a858', 'all'),
@@ -95,13 +95,13 @@
     # Keep the revision in sync with Chrome's PACKAGE_VERSION in
     # tools/clang/scripts/update.py.
     ('buildtools/clang.tgz',
-     'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-365097-f7e52fbd-8.tgz',
-     'fe1b1e5bd7381ae655661cb9658487389561568d', 'linux'),
+     'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-n332890-c2443155-2.tgz',
+     'd6501ffdb5dbb0ffe8a4b873cc092a9929e661ec', 'linux'),
 
     # Keep in sync with chromium DEPS.
     ('buildtools/libfuzzer',
      'https://chromium.googlesource.com/chromium/llvm-project/compiler-rt/lib/fuzzer.git',
-     'b9f51dc8c98065df0c8da13c051046f5bab833db', 'linux'),
+     'debe7d2d1982e540fbd6bd78604bf001753f9e74', 'linux'),
 
     # Benchmarking tool.
     ('buildtools/benchmark.zip',
@@ -132,7 +132,7 @@
     # These dependencies are for libunwindstack, which is used by src/profiling.
     ('buildtools/android-core',
      'https://android.googlesource.com/platform/system/core.git',
-     '3f407fcc37b401c91784700c0a691ba8b1f7ef15', 'all'),
+     '8bf4e29e44098e3232ff646331675fb113064162', 'all'),
     ('buildtools/lzma',
      'https://android.googlesource.com/platform/external/lzma.git',
      '7851dce6f4ca17f5caa1c93a4e0a45686b1d56c3', 'all'),
@@ -146,8 +146,8 @@
     # Example traces for regression tests.
     (
         'buildtools/test_data.zip',
-        'https://storage.googleapis.com/perfetto/test-data-20191107-164334.zip',
-        '499f11fbc2b04ef7742662a26b85ef03141e24bd',
+        'https://storage.googleapis.com/perfetto/test-data-20200122-100845.zip',
+        '56ac45b5239fda50d33cf05bfd329dcb3efb0b2a',
         'all',
     ),
 
diff --git a/tools/protoprofile/BUILD.gn b/tools/protoprofile/BUILD.gn
index d23cbf8..48fbc85 100644
--- a/tools/protoprofile/BUILD.gn
+++ b/tools/protoprofile/BUILD.gn
@@ -30,10 +30,6 @@
     "../../src/base",
     "../../src/protozero",
   ]
-  sources = [
-    "main.cc",
-  ]
-  deps = [
-    "../../gn:protobuf_full",
-  ]
+  sources = [ "main.cc" ]
+  deps = [ "../../gn:protobuf_full" ]
 }
diff --git a/tools/sanitizers_unittests/BUILD.gn b/tools/sanitizers_unittests/BUILD.gn
index be69e1a..26ef638 100644
--- a/tools/sanitizers_unittests/BUILD.gn
+++ b/tools/sanitizers_unittests/BUILD.gn
@@ -18,7 +18,5 @@
     "../../gn:default_deps",
     "../../gn:gtest_and_gmock",
   ]
-  sources = [
-    "sanitizers_unittest.cc",
-  ]
+  sources = [ "sanitizers_unittest.cc" ]
 }
diff --git a/tools/skippy/BUILD.gn b/tools/skippy/BUILD.gn
index 087c2ad1..b2926c0 100644
--- a/tools/skippy/BUILD.gn
+++ b/tools/skippy/BUILD.gn
@@ -18,7 +18,5 @@
     "../../gn:default_deps",
     "../../src/base",
   ]
-  sources = [
-    "skippy.cc",
-  ]
+  sources = [ "skippy.cc" ]
 }
diff --git a/tools/test_data.txt b/tools/test_data.txt
index d2dd686..abcb65d 100644
--- a/tools/test_data.txt
+++ b/tools/test_data.txt
@@ -2,3 +2,4 @@
 # to the root.
 src/traced/probes/ftrace/test/data/
 src/traced/probes/filesystem/testdata/
+test/data/kallsyms.txt
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 41c844d..bb0cf7b 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -50,10 +50,12 @@
     "../../src/profiling/symbolizer:symbolize_database",
   ]
   public_deps = [
-    "../../gn:zlib",
     "../../include/perfetto/ext/base",
     "../../include/perfetto/profiling:deobfuscator",
   ]
+  if (enable_perfetto_zlib) {
+    public_deps += [ "../../gn:zlib" ]
+  }
   sources = [
     "utils.cc",
     "utils.h",
@@ -61,9 +63,7 @@
 }
 
 source_set("pprofbuilder") {
-  public_deps = [
-    "../../include/perfetto/profiling:pprof_builder",
-  ]
+  public_deps = [ "../../include/perfetto/profiling:pprof_builder" ]
   deps = [
     ":utils",
     "../../gn:default_deps",
@@ -76,17 +76,13 @@
     "../../src/profiling/symbolizer",
     "../../src/profiling/symbolizer:symbolize_database",
   ]
-  sources = [
-    "pprof_builder.cc",
-  ]
+  sources = [ "pprof_builder.cc" ]
 }
 
 # Exposed in bazel builds.
 static_library("libpprofbuilder") {
   complete_static_lib = true
-  deps = [
-    ":pprofbuilder",
-  ]
+  deps = [ ":pprofbuilder" ]
 }
 
 # The core source files that are used both by the "full" version (the host
@@ -131,9 +127,7 @@
     "../../gn:default_deps",
     "../../include/perfetto/base",
   ]
-  sources = [
-    "lite_fallbacks.cc",
-  ]
+  sources = [ "lite_fallbacks.cc" ]
 }
 
 # Full target for the host. Depends on libprotobuf-full.
@@ -144,9 +138,11 @@
     ":utils",
     "../../gn:default_deps",
     "../../gn:protobuf_full",
-    "../../gn:zlib",
     "../../protos/perfetto/trace:zero",
   ]
+  if (enable_perfetto_zlib) {
+    deps += [ "../../gn:zlib" ]
+  }
   sources = [
     "proto_full_utils.cc",
     "proto_full_utils.h",
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
index 4da0d9d..4f57829 100644
--- a/tools/trace_to_text/pprof_builder.cc
+++ b/tools/trace_to_text/pprof_builder.cc
@@ -214,7 +214,8 @@
         max_symbol_id_(max_symbol_id) {
     // The pprof format expects the first entry in the string table to be the
     // empty string.
-    Intern("");
+    int64_t empty_id = Intern("");
+    PERFETTO_CHECK(empty_id == 0);
   }
 
   std::vector<Iterator> BuildViewIterators(trace_processor::TraceProcessor* tp,
@@ -375,7 +376,9 @@
       std::string frame_name = frame_it.Get(1).string_value;
       int64_t mapping_id = frame_it.Get(2).long_value;
       int64_t rel_pc = frame_it.Get(3).long_value;
-      int64_t symbol_set_id = frame_it.Get(4).long_value;
+      base::Optional<int64_t> symbol_set_id;
+      if (!frame_it.Get(4).is_null())
+        symbol_set_id = frame_it.Get(4).long_value;
 
       seen_mappings->emplace(mapping_id);
       auto* glocation = result_->add_location();
@@ -386,7 +389,7 @@
       //                           mapping.start_offset)).
       glocation->set_address(static_cast<uint64_t>(rel_pc));
       if (symbol_set_id) {
-        for (const Line& line : LineForSymbolSetId(symbol_set_id)) {
+        for (const Line& line : LineForSymbolSetId(*symbol_set_id)) {
           seen_symbol_ids->emplace(line.symbol_id);
           auto* gline = glocation->add_line();
           gline->set_line(line.line_number);
diff --git a/tools/trace_to_text/trace_to_text.cc b/tools/trace_to_text/trace_to_text.cc
index 3fb86d8..1616a2d 100644
--- a/tools/trace_to_text/trace_to_text.cc
+++ b/tools/trace_to_text/trace_to_text.cc
@@ -16,8 +16,6 @@
 
 #include "tools/trace_to_text/trace_to_text.h"
 
-#include <zlib.h>
-
 #include <google/protobuf/compiler/importer.h>
 #include <google/protobuf/dynamic_message.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -31,6 +29,10 @@
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#endif
+
 namespace perfetto {
 namespace trace_to_text {
 
@@ -78,6 +80,7 @@
 void PrintCompressedPackets(const std::string& packets,
                             Message* compressed_msg_scratch,
                             ZeroCopyOutputStream* output) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
   uint8_t out[4096];
   std::vector<uint8_t> data;
 
@@ -120,6 +123,24 @@
   }
   WriteToZeroCopyOutput(output, kCompressedPacketsSuffix,
                         sizeof(kCompressedPacketsSuffix) - 1);
+#else
+  base::ignore_result(packets);
+  base::ignore_result(compressed_msg_scratch);
+  base::ignore_result(kIndentedPacketPrefix);
+  base::ignore_result(kIndentedPacketSuffix);
+  WriteToZeroCopyOutput(output, kCompressedPacketsPrefix,
+                        sizeof(kCompressedPacketsPrefix) - 1);
+  static const char kErrMsg[] =
+      "Cannot decode compressed packets. zlib not enabled in the build config";
+  WriteToZeroCopyOutput(output, kErrMsg, sizeof(kErrMsg) - 1);
+  WriteToZeroCopyOutput(output, kCompressedPacketsSuffix,
+                        sizeof(kCompressedPacketsSuffix) - 1);
+  static bool log_once = [] {
+    PERFETTO_ELOG("%s", kErrMsg);
+    return true;
+  }();
+  base::ignore_result(log_once);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 }
 
 }  // namespace
diff --git a/tools/trace_to_text/utils.cc b/tools/trace_to_text/utils.cc
index 73645f6..e655b38 100644
--- a/tools/trace_to_text/utils.cc
+++ b/tools/trace_to_text/utils.cc
@@ -39,8 +39,9 @@
 
 using Iterator = trace_processor::TraceProcessor::Iterator;
 
-
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 constexpr size_t kCompressionBufferSize = 500 * 1024;
+#endif
 
 std::map<std::string, std::set<std::string>> GetHeapGraphClasses(
     trace_processor::TraceProcessor* tp) {
@@ -179,7 +180,6 @@
   return true;
 }
 
-
 void DeobfuscateDatabase(
     trace_processor::TraceProcessor* tp,
     const std::map<std::string, profiling::ObfuscatedClass>& mapping,
@@ -229,6 +229,8 @@
   output_->write(data, static_cast<std::streamsize>(sz));
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 DeflateTraceWriter::DeflateTraceWriter(std::ostream* output)
     : TraceWriter(output),
       buf_(base::PagedMemory::Allocate(kCompressionBufferSize)),
@@ -270,6 +272,15 @@
   PERFETTO_FATAL("Expected %d got %d: %s", actual_code, expected_code,
                  stream_.msg);
 }
+#else
+
+DeflateTraceWriter::DeflateTraceWriter(std::ostream* output)
+    : TraceWriter(output) {
+  PERFETTO_ELOG("Cannot compress. Zlib is not enabled in the build config");
+}
+DeflateTraceWriter::~DeflateTraceWriter() = default;
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 
 }  // namespace trace_to_text
 }  // namespace perfetto
diff --git a/tools/trace_to_text/utils.h b/tools/trace_to_text/utils.h
index 19bd3ca..d708e04 100644
--- a/tools/trace_to_text/utils.h
+++ b/tools/trace_to_text/utils.h
@@ -26,13 +26,15 @@
 #include <memory>
 #include <vector>
 
-#include <zlib.h>
-
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/profiling/deobfuscator.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#endif
+
 namespace perfetto {
 
 namespace trace_processor {
@@ -83,6 +85,7 @@
   std::ostream* output_;
 };
 
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 class DeflateTraceWriter : public TraceWriter {
  public:
   DeflateTraceWriter(std::ostream* output);
@@ -100,6 +103,17 @@
   uint8_t* const end_;
 };
 
+#else
+
+// Fallback implementation. Will print an error and write uncompressed.
+class DeflateTraceWriter : public TraceWriter {
+ public:
+  DeflateTraceWriter(std::ostream* output);
+  ~DeflateTraceWriter() override;
+};
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
 }  // namespace trace_to_text
 }  // namespace perfetto
 
diff --git a/ui/BUILD.gn b/ui/BUILD.gn
index b27e380..b488557 100644
--- a/ui/BUILD.gn
+++ b/ui/BUILD.gn
@@ -28,23 +28,44 @@
 # +----------------------------------------------------------------------------+
 # | The outer "ui" target to just ninja -C out/xxx ui                          |
 # +----------------------------------------------------------------------------+
+
 group("ui") {
   deps = [
-    ":assets_dist",
-    ":catapult_dist",
     ":chrome_extension_assets_dist",
     ":chrome_extension_bundle_dist",
-    ":controller_bundle_dist",
-    ":engine_bundle_dist",
-    ":frontend_bundle_dist",
-    ":index_dist",
-    ":scss",
+    ":dist",
+    ":gen_dist_file_map",
+    ":service_worker_bundle_dist",
     ":test_scripts",
-    ":typefaces_dist",
-    ":wasm_dist",
+
+    # IMPORTANT: Only add deps here if they are NOT part of the production UI
+    # (e.g., tests, extensions, ...). Any UI dep should go in the
+    # |ui_dist_targets| list below. The only exception is the service worker
+    # target, that depends on that list.
   ]
 }
 
+# The list of targets that produces dist/ files for the UI. This list is used
+# also by the gen_dist_file_map to generate the map of hashes of all UI files,
+# which is turn used by the service worker code for the offline caching.
+ui_dist_targets = [
+  ":assets_dist",
+  ":catapult_dist",
+  ":controller_bundle_dist",
+  ":engine_bundle_dist",
+  ":frontend_bundle_dist",
+  ":index_dist",
+  ":scss",
+  ":typefaces_dist",
+  ":wasm_dist",
+]
+
+# Buils the ui, but not service worker, tests and extensions.
+group("dist") {
+  deps = ui_dist_targets
+}
+
+# A minimal page to profile the WASM engine without the all UI.
 group("query") {
   deps = [
     ":query_bundle_dist",
@@ -64,9 +85,7 @@
                              "outputs",
                              "depfile",
                            ])
-    deps = [
-      ":node_modules",
-    ]
+    deps = [ ":node_modules" ]
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
@@ -95,9 +114,7 @@
     assert(defined(invoker.input))
     assert(defined(invoker.output))
     forward_variables_from(invoker, [ "deps" ])
-    inputs = [
-      invoker.input,
-    ]
+    inputs = [ invoker.input ]
     outputs = [
       invoker.output,
       invoker.output + ".map",
@@ -151,41 +168,37 @@
 # included by the .html files.
 
 bundle("frontend_bundle") {
-  deps = [
-    ":transpile_all_ts",
-  ]
+  deps = [ ":transpile_all_ts" ]
   input = "$target_out_dir/frontend/index.js"
   output = "$target_out_dir/frontend_bundle.js"
 }
 
 bundle("chrome_extension_bundle") {
-  deps = [
-    ":transpile_all_ts",
-  ]
+  deps = [ ":transpile_all_ts" ]
   input = "$target_out_dir/chrome_extension/index.js"
   output = "$target_out_dir/chrome_extension_bundle.js"
 }
 
 bundle("controller_bundle") {
-  deps = [
-    ":transpile_all_ts",
-  ]
+  deps = [ ":transpile_all_ts" ]
   input = "$target_out_dir/controller/index.js"
   output = "$target_out_dir/controller_bundle.js"
 }
 
 bundle("engine_bundle") {
-  deps = [
-    ":transpile_all_ts",
-  ]
+  deps = [ ":transpile_all_ts" ]
   input = "$target_out_dir/engine/index.js"
   output = "$target_out_dir/engine_bundle.js"
 }
 
+bundle("service_worker_bundle") {
+  deps = [ ":transpile_service_worker_ts" ]
+  input = "$target_out_dir/service_worker/service_worker.js"
+  output = "$target_out_dir/service_worker.js"
+}
+
 bundle("query_bundle") {
-  deps = [
-    ":transpile_all_ts",
-  ]
+  deps = [ ":transpile_all_ts" ]
   input = "$target_out_dir/query/index.js"
   output = "$target_out_dir/query_bundle.js"
 }
@@ -203,9 +216,7 @@
     "../protos/perfetto/ipc/consumer_port.proto",
     "../protos/perfetto/ipc/wire_protocol.proto",
   ]
-  outputs = [
-    "$ui_gen_dir/protos.js",
-  ]
+  outputs = [ "$ui_gen_dir/protos.js" ]
   node_cmd = "pbjs"
   args = [
            "-t",
@@ -222,15 +233,9 @@
 # Protobuf.js requires to first generate .js files from the .proto and then
 # create .ts definitions for them.
 node_bin("protos_to_ts") {
-  deps = [
-    ":protos_to_js",
-  ]
-  inputs = [
-    "$ui_gen_dir/protos.js",
-  ]
-  outputs = [
-    "$ui_gen_dir/protos.d.ts",
-  ]
+  deps = [ ":protos_to_js" ]
+  inputs = [ "$ui_gen_dir/protos.js" ]
+  outputs = [ "$ui_gen_dir/protos.d.ts" ]
   node_cmd = "pbts"
   args = [
     "-p",
@@ -252,9 +257,7 @@
     ":protos_to_ts",
     ":wasm_gen",
   ]
-  inputs = [
-    "tsconfig.json",
-  ]
+  inputs = [ "tsconfig.json" ]
   outputs = [
     "$target_out_dir/frontend/index.js",
     "$target_out_dir/engine/index.js",
@@ -270,6 +273,7 @@
                 "--filter=*.ts",
                 "--exclude=node_modules",
                 "--exclude=dist",
+                "--exclude=service_worker",
                 "--deps=obj/ui/frontend/index.js",
                 "--output=" + rebase_path(depfile),
               ],
@@ -284,6 +288,26 @@
   ]
 }
 
+node_bin("transpile_service_worker_ts") {
+  deps = [
+    ":dist_symlink",
+    ":gen_dist_file_map",
+  ]
+  inputs = [
+    "tsconfig.json",
+    "src/service_worker/service_worker.ts",
+  ]
+  outputs = [ "$target_out_dir/service_worker/service_worker.js" ]
+
+  node_cmd = "tsc"
+  args = [
+    "--project",
+    rebase_path("src/service_worker", root_build_dir),
+    "--outDir",
+    rebase_path(target_out_dir, root_build_dir),
+  ]
+}
+
 # +----------------------------------------------------------------------------+
 # | Build css.                                                                 |
 # +----------------------------------------------------------------------------+
@@ -301,13 +325,9 @@
 
 # Build css.
 node_bin("scss") {
-  deps = [
-    ":dist_symlink",
-  ]
+  deps = [ ":dist_symlink" ]
   inputs = [ scss_root ] + scss_srcs
-  outputs = [
-    "$ui_dir/perfetto.css",
-  ]
+  outputs = [ "$ui_dir/perfetto.css" ]
 
   node_cmd = "node-sass"
   args = [
@@ -321,12 +341,8 @@
 # | Copy rules: create the final output directory.                             |
 # +----------------------------------------------------------------------------+
 copy("index_dist") {
-  sources = [
-    "index.html",
-  ]
-  outputs = [
-    "$ui_dir/index.html",
-  ]
+  sources = [ "index.html" ]
+  outputs = [ "$ui_dir/index.html" ]
 }
 
 copy("typefaces_dist") {
@@ -339,18 +355,12 @@
     "../buildtools/typefaces/RobotoMono-Regular.woff2",
   ]
 
-  outputs = [
-    "$ui_dir/assets/{{source_file_part}}",
-  ]
+  outputs = [ "$ui_dir/assets/{{source_file_part}}" ]
 }
 
 copy("query_dist") {
-  sources = [
-    "query.html",
-  ]
-  outputs = [
-    "$ui_dir/query.html",
-  ]
+  sources = [ "query.html" ]
+  outputs = [ "$ui_dir/query.html" ]
 }
 
 copy("assets_dist") {
@@ -377,9 +387,7 @@
               "src/assets/rec_ring_buf.png",
               "src/assets/rec_vmstat.png",
             ] + [ scss_root ] + scss_srcs
-  outputs = [
-    "$ui_dir/assets/{{source_file_part}}",
-  ]
+  outputs = [ "$ui_dir/assets/{{source_file_part}}" ]
 }
 copy("chrome_extension_assets_dist") {
   sources = [
@@ -387,47 +395,41 @@
     "src/assets/logo.png",
     "src/chrome_extension/manifest.json",
   ]
-  outputs = [
-    "$chrome_extension_dir/{{source_file_part}}",
-  ]
+  outputs = [ "$chrome_extension_dir/{{source_file_part}}" ]
 }
 
 sorcery("frontend_bundle_dist") {
-  deps = [
-    ":frontend_bundle",
-  ]
+  deps = [ ":frontend_bundle" ]
   input = "$target_out_dir/frontend_bundle.js"
   output = "$ui_dir/frontend_bundle.js"
 }
 
 sorcery("chrome_extension_bundle_dist") {
-  deps = [
-    ":chrome_extension_bundle",
-  ]
+  deps = [ ":chrome_extension_bundle" ]
   input = "$target_out_dir/chrome_extension_bundle.js"
   output = "$chrome_extension_dir/chrome_extension_bundle.js"
 }
 
 sorcery("controller_bundle_dist") {
-  deps = [
-    ":controller_bundle",
-  ]
+  deps = [ ":controller_bundle" ]
   input = "$target_out_dir/controller_bundle.js"
   output = "$ui_dir/controller_bundle.js"
 }
 
 sorcery("engine_bundle_dist") {
-  deps = [
-    ":engine_bundle",
-  ]
+  deps = [ ":engine_bundle" ]
   input = "$target_out_dir/engine_bundle.js"
   output = "$ui_dir/engine_bundle.js"
 }
 
+sorcery("service_worker_bundle_dist") {
+  deps = [ ":service_worker_bundle" ]
+  input = "$target_out_dir/service_worker.js"
+  output = "$ui_dir/service_worker.js"
+}
+
 sorcery("query_bundle_dist") {
-  deps = [
-    ":query_bundle",
-  ]
+  deps = [ ":query_bundle" ]
   input = "$target_out_dir/query_bundle.js"
   output = "$ui_dir/query_bundle.js"
 }
@@ -441,9 +443,7 @@
     "$root_build_dir/wasm/trace_processor.wasm",
     "$root_build_dir/wasm/trace_to_text.wasm",
   ]
-  outputs = [
-    "$ui_dir/{{source_file_part}}",
-  ]
+  outputs = [ "$ui_dir/{{source_file_part}}" ]
 }
 
 copy("wasm_gen") {
@@ -477,9 +477,7 @@
       "$root_build_dir/wasm/trace_to_text.wasm.map",
     ]
   }
-  outputs = [
-    "$ui_gen_dir/{{source_file_part}}",
-  ]
+  outputs = [ "$ui_gen_dir/{{source_file_part}}" ]
 }
 
 # Copy over the vulcanized legacy trace viewer.
@@ -488,9 +486,7 @@
     "../buildtools/catapult_trace_viewer/catapult_trace_viewer.html",
     "../buildtools/catapult_trace_viewer/catapult_trace_viewer.js",
   ]
-  outputs = [
-    "$ui_dir/assets/{{source_file_part}}",
-  ]
+  outputs = [ "$ui_dir/assets/{{source_file_part}}" ]
 }
 
 # +----------------------------------------------------------------------------+
@@ -505,17 +501,13 @@
     rebase_path("$target_out_dir/node_exists", ""),
   ]
   inputs = []
-  outputs = [
-    "$target_out_dir/node_exists",
-  ]
+  outputs = [ "$target_out_dir/node_exists" ]
 }
 
 # Creates a symlink from out/xxx/ui/node_modules -> ../../../ui/node_modules.
 # This allows to run rollup and other node tools from the out/xxx directory.
 action("node_modules_symlink") {
-  deps = [
-    ":check_node_exists",
-  ]
+  deps = [ ":check_node_exists" ]
 
   script = "../gn/standalone/build_tool_wrapper.py"
   stamp_file = "$target_out_dir/.$target_name.stamp"
@@ -527,15 +519,11 @@
     rebase_path("node_modules", target_out_dir),
     rebase_path("$target_out_dir/node_modules", root_build_dir),
   ]
-  outputs = [
-    stamp_file,
-  ]
+  outputs = [ stamp_file ]
 }
 
 group("node_modules") {
-  deps = [
-    ":node_modules_symlink",
-  ]
+  deps = [ ":node_modules_symlink" ]
 }
 
 # Creates a symlink from //ui/dist -> ../../out/xxx/ui. Used only for
@@ -553,12 +541,8 @@
     rebase_path(target_out_dir, "."),
     rebase_path("dist", root_build_dir),
   ]
-  inputs = [
-    "$root_build_dir",
-  ]
-  outputs = [
-    stamp_file,
-  ]
+  inputs = [ "$root_build_dir" ]
+  outputs = [ stamp_file ]
 }
 
 group("test_scripts") {
@@ -569,19 +553,36 @@
 }
 
 copy("copy_unittests_script") {
-  sources = [
-    "config/ui_unittests_template",
-  ]
-  outputs = [
-    "$root_build_dir/ui_unittests",
-  ]
+  sources = [ "config/ui_unittests_template" ]
+  outputs = [ "$root_build_dir/ui_unittests" ]
 }
 
 copy("copy_tests_script") {
-  sources = [
-    "config/ui_tests_template",
-  ]
-  outputs = [
-    "$root_build_dir/ui_tests",
-  ]
+  sources = [ "config/ui_tests_template" ]
+  outputs = [ "$root_build_dir/ui_tests" ]
+}
+
+# This target generates an map containing all the UI subresources and their
+# hashes. This is used by the service worker code for offline caching.
+# This taarget needs to be kept at the end of the BUILD.gn file, because of the
+# get_target_outputs() call (fails otherwise due to GN's evaluation order).
+action("gen_dist_file_map") {
+  out_file_path = "$ui_gen_dir/dist_file_map.ts"
+
+  dist_files = []
+  foreach(target, ui_dist_targets) {
+    foreach(dist_file, get_target_outputs(target)) {
+      dist_files += [ rebase_path(dist_file, root_build_dir) ]
+    }
+  }
+  deps = [ ":dist" ]
+  script = "../gn/standalone/write_ui_dist_file_map.py"
+  inputs = []
+  outputs = [ out_file_path ]
+  args = [
+           "--out",
+           rebase_path(out_file_path, root_build_dir),
+           "--strip",
+           rebase_path(ui_dir, root_build_dir),
+         ] + dist_files
 }
diff --git a/ui/index.html b/ui/index.html
index 0e1833a..6fa32a6 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -6,11 +6,11 @@
   <!-- WebComponents V0 origin trial token for https://ui.perfetto.dev Expires 17 Dec 2020.
   See https://crbug.com/1021137. -->
   <meta http-equiv="origin-trial" content="AtzsILqIzNPGftktQTEYxI9GpnqFBuse5uB5n4JQO3Wa1ky4TCKmnXZli0A9g9p7Es7Il9pqarELntnfm0HriwkAAABreyJvcmlnaW4iOiJodHRwczovL3VpLnBlcmZldHRvLmRldjo0NDMiLCJmZWF0dXJlIjoiV2ViQ29tcG9uZW50c1YwIiwiZXhwaXJ5IjoxNjA4MjI2NDQzLCJpc1N1YmRvbWFpbiI6dHJ1ZX0=">
+  <!-- WebComponents V0 origin trial token for http://localhost:10000 Expires 28 Jan 2021.
+  See https://crbug.com/1021137. -->
+  <meta http-equiv="origin-trial" content="AicMEv5glMGL1lq6ZRsxFJj8xlhn3XDYZrHK0/2KreAD/r62vTFjUBOueeMTxWuU1IlRXqCugRFDD7rY45YEgwkAAABTeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjEwMDAwIiwiZmVhdHVyZSI6IldlYkNvbXBvbmVudHNWMCIsImV4cGlyeSI6MTYxMTg0MDczNH0=">
   <link href="perfetto.css" rel="stylesheet">
   <link rel="icon" type="image/png" href="assets/logo.png">
-  <link rel="preload" href="controller_bundle.js" as="script">
-  <link rel="preload" href="engine_bundle.js" as="script">
-  <link rel="preload" href="trace_processor.wasm" as="fetch">
 </head>
 <body>
   <main>
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
index 7600ebd..d5d0e6a 100644
--- a/ui/src/assets/record.scss
+++ b/ui/src/assets/record.scss
@@ -136,12 +136,12 @@
         border: 1px solid #eee;
         outline: none;
         margin: 4px;
-        border-radius: 10px;
+        border-radius: 20px;
         padding: 4px;
         height: 30px;
         &:hover, &:active {
-          box-shadow: 0 0 4px 0px #999;
-          background-color: hsl(88, 50%, 84%);
+          box-shadow: 0 0 4px 0px #ccc;
+          background-color: #fafafa;
         }
         i {
           margin: 3px;
@@ -274,6 +274,7 @@
   transition: opacity 0.25s ease;
   opacity: 0;
   visibility: hidden;
+  overflow: auto;
 
   &:not(.active) {
     max-height: 0;
@@ -693,7 +694,6 @@
 
       &.two-columns {
         height: 400px;
-        width: auto;
         margin: var(--record-section-padding);
         optgroup {
           display: grid;
@@ -719,10 +719,6 @@
     height: 152px;
   }
 
-  .chrome-categories {
-    height: 152px;
-  }
-
   textarea.extra-input {
     width: 100%;
     height: 60px;
@@ -774,6 +770,8 @@
       font-size: 12px;
       line-height: 20px;
       overflow-y: auto;
+      white-space: pre-wrap;
+      word-wrap: break-word;
 
       // 510px and not 500px, so the overflowing line gets truncated, giving
       // a clear indication that the code box scrolls.
diff --git a/ui/src/assets/sidebar.scss b/ui/src/assets/sidebar.scss
index 7cb1ce2..7868f27 100644
--- a/ui/src/assets/sidebar.scss
+++ b/ui/src/assets/sidebar.scss
@@ -184,7 +184,7 @@
         }
       }
 
-      > .num-queued-queries {
+      > .dbg-info-square {
         width: 24px;
         height: 22px;
         line-height: 22px;
@@ -194,11 +194,15 @@
         border-radius: 5px;
         font-size: 12px;
         text-align: center;
-        &.rpc {
+        &.green {
           background: #7aca75;
           color: #12161b;
         }
-        &.failed {
+        &.amber {
+          background: #FFC107;
+          color: #333;
+        }
+        &.red {
           background: #d32f2f;
           color: #fff;
         }
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index 49c06df..03a907f 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -201,4 +201,50 @@
             right: -90%;
         }
     }
+
+    .notification-btn {
+        @include transition(0.25s);
+        font-size: 16px;
+        padding: 8px 10px;
+        margin: 0 10px;
+        border-radius: 2px;
+        background: hsl(210, 10%, 73%);
+        &:hover {
+            background: hsl(210, 10%, 83%);
+        }
+
+        &.preferred {
+            background: hsl(210, 98%, 53%);
+            color: #fff;
+            &:hover {
+                background: hsl(210, 98%, 63%);
+            }
+        }
+    }
+}
+
+.helpful-hint {
+  position: absolute;
+  right: 5px;
+  top: 5px;
+  width: 300px;
+  background-color: white;
+  font-size: 12px;
+  color: #3f4040;
+  display: grid;
+  border-radius: 5px;
+  padding: 8px;
+  box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
+}
+
+.hint-text {
+  padding-bottom: 5px
+}
+
+.hint-dismiss-button {
+  color: #f4fafb;
+  background-color: #19212b;
+  width: fit-content;
+  padding: 3px;
+  border-radius: 3px;
 }
diff --git a/ui/src/chrome_extension/chrome_tracing_controller.ts b/ui/src/chrome_extension/chrome_tracing_controller.ts
index 356bd44..280068a 100644
--- a/ui/src/chrome_extension/chrome_tracing_controller.ts
+++ b/ui/src/chrome_extension/chrome_tracing_controller.ts
@@ -208,8 +208,14 @@
       fetchCategories();
       return;
     }
-    // Otherwise, we find attach to the target temporarily.
-    this.devtoolsSocket.findAndAttachTarget(async _ => {
+    // Otherwise, we attach temporarily.
+    this.devtoolsSocket.attachToBrowser(async (error?: string) => {
+      if (error) {
+        this.sendErrorMessage(
+            `Could not attach to DevTools browser target ` +
+            `(req. Chrome >= M81): ${error}`);
+        return;
+      }
       fetchCategories();
       this.devtoolsSocket.detach();
     });
@@ -230,7 +236,13 @@
   }
 
   handleStartTracing(traceConfig: Protocol.Tracing.TraceConfig) {
-    this.devtoolsSocket.findAndAttachTarget(async _ => {
+    this.devtoolsSocket.attachToBrowser(async (error?: string) => {
+      if (error) {
+        this.sendErrorMessage(
+            `Could not attach to DevTools browser target ` +
+            `(req. Chrome >= M81): ${error}`);
+        return;
+      }
       await this.api.Tracing.start({
         traceConfig,
         streamFormat: 'proto',
diff --git a/ui/src/chrome_extension/devtools_socket.ts b/ui/src/chrome_extension/devtools_socket.ts
index aa23f8f..bb40f5c 100644
--- a/ui/src/chrome_extension/devtools_socket.ts
+++ b/ui/src/chrome_extension/devtools_socket.ts
@@ -64,25 +64,20 @@
     throw new Error('Call unexpected');
   }
 
-  findTarget(then: (target: chrome.debugger.Debuggee) => void) {
-    chrome.debugger.getTargets(targets => {
-      const perfettoTab =
-          targets.find(target => target.title.includes('Perfetto'));
-      if (perfettoTab === undefined) {
-        console.log('No perfetto tab found');
-        return;
-      }
-      this.target = {targetId: perfettoTab.id};
-      then(this.target);
-    });
+  attachToBrowser(then: (error?: string) => void) {
+    this.attachToTarget({targetId: 'browser'}, then);
   }
 
-  findAndAttachTarget(then: (target: chrome.debugger.Debuggee) => void) {
-    this.findTarget(t => {
-      chrome.debugger.attach(t, /*requiredVersion=*/ '1.3', () => {
-        this.openCallback();
-        then(t);
-      });
+  private attachToTarget(
+      target: chrome.debugger.Debuggee, then: (error?: string) => void) {
+    chrome.debugger.attach(target, /*requiredVersion=*/ '1.3', () => {
+      if (chrome.runtime.lastError) {
+        then(chrome.runtime.lastError.message);
+        return;
+      }
+      this.target = target;
+      this.openCallback();
+      then();
     });
   }
 
diff --git a/ui/src/chrome_extension/index.ts b/ui/src/chrome_extension/index.ts
index 2d8f4fc..6350c84 100644
--- a/ui/src/chrome_extension/index.ts
+++ b/ui/src/chrome_extension/index.ts
@@ -54,7 +54,7 @@
   }
   chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
     chrome.declarativeContent.onPageChanged.addRules([
-      enableOnHostWithSuffix('.perfetto.local'),
+      enableOnHostWithSuffix('localhost'),
       enableOnHostWithSuffix('.perfetto.dev'),
     ]);
   });
diff --git a/ui/src/chrome_extension/manifest.json b/ui/src/chrome_extension/manifest.json
index 5abd7bb..5008c01 100644
--- a/ui/src/chrome_extension/manifest.json
+++ b/ui/src/chrome_extension/manifest.json
@@ -2,8 +2,9 @@
   "name": "Perfetto UI",
   "key":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhm3X7qutsrskke84ltokTObnFJakd/d0XFQ6Ox2wQueHTGJM5GUNPTY/x8bdreNtGnfzvt/Sd0vABbR0wsS6lz5yY+g6ksMXJnigFe9N7uz8E3KojDrl3xYjIe+mkiJo8yxxzPydgb7GjQ6jmsX3g+yjj67kXzm9rZFkmoZ5WmqwBZlguPYVRN/W8CIIqBZkC3Qmq6uSG7b/g93YbwqmTmGiL2sAzgvXtqvDOD6503abtQkRC795E4VjJd+ffyeRH38fAEz5ZIrA6GJsfmov1TZTIu1NTwqylSpBYl5as7C6gpmuxDV4SvHvGT2hMQuIufDhZhErjI3B7bcX+XLe1wIDAQAB",
   "description": "Enables the Perfetto trace viewer (https://ui.perfetto.dev) to record Chrome browser traces.",
-  "version": "0.0.0.9",
+  "version": "0.0.0.11",
   "manifest_version": 2,
+  "minimum_chrome_version": "81.0.4022.0",
   "permissions": [
     "declarativeContent",
     "debugger"
@@ -19,7 +20,7 @@
   },
   "externally_connectable": {
     "matches": [
-      "*://*.perfetto.local/*",
+      "*://localhost/*",
       "https://*.perfetto.dev/*"
     ]
   }
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index c5f7f63..8e43ca5 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -426,18 +426,26 @@
       },
 
   selectHeapProfile(
-      state: StateDraft, args: {id: number, upid: number, ts: number}): void {
-    state.currentSelection =
-        {kind: 'HEAP_PROFILE', id: args.id, upid: args.upid, ts: args.ts};
+      state: StateDraft,
+      args: {id: number, upid: number, ts: number, type: string}): void {
+    state.currentSelection = {
+      kind: 'HEAP_PROFILE',
+      id: args.id,
+      upid: args.upid,
+      ts: args.ts,
+      type: args.type
+    };
   },
 
   showHeapProfileFlamegraph(
-      state: StateDraft, args: {id: number, upid: number, ts: number}): void {
+      state: StateDraft,
+      args: {id: number, upid: number, ts: number, type: string}): void {
     state.currentHeapProfileFlamegraph = {
       kind: 'HEAP_PROFILE_FLAMEGRAPH',
       id: args.id,
       upid: args.upid,
       ts: args.ts,
+      type: args.type,
     };
   },
 
diff --git a/ui/src/common/flamegraph_unittest.ts b/ui/src/common/flamegraph_unittest.ts
index fc19250..6b3e178 100644
--- a/ui/src/common/flamegraph_unittest.ts
+++ b/ui/src/common/flamegraph_unittest.ts
@@ -162,7 +162,7 @@
     {
       id: 2,
       parentId: 1,
-      name: 'A2',
+      name: '[merged]',
       depth: 1,
       totalSize: 10,
       selfSize: 0,
@@ -269,7 +269,7 @@
     {
       id: 3,
       parentId: 1,
-      name: 'A3',
+      name: '[merged]',
       depth: 1,
       totalSize: 5,
       selfSize: 0,
@@ -278,7 +278,7 @@
     {
       id: 6,
       parentId: 3,
-      name: 'A36',
+      name: '[merged]',
       depth: 2,
       totalSize: 3,
       selfSize: 0,
@@ -392,7 +392,7 @@
     {
       id: 3,
       parentId: 1,
-      name: 'A3',
+      name: '[merged]',
       depth: 1,
       totalSize: 5,
       selfSize: 0,
@@ -507,7 +507,7 @@
     {
       id: 3,
       parentId: 1,
-      name: 'A3',
+      name: '[merged]',
       depth: 1,
       totalSize: 4,
       selfSize: 0,
@@ -597,7 +597,7 @@
     {
       id: 1,
       parentId: -1,
-      name: 'A',
+      name: '[merged]',
       depth: 0,
       totalSize: 12,
       selfSize: 0,
@@ -821,7 +821,7 @@
     {
       id: 4,
       parentId: 1,
-      name: 'A4',
+      name: '[merged]',
       depth: 1,
       totalSize: 35,
       selfSize: 0,
@@ -857,7 +857,7 @@
     {
       id: 10,
       parentId: 4,
-      name: 'A410',
+      name: '[merged]',
       depth: 2,
       totalSize: 25,
       selfSize: 0,
@@ -866,7 +866,7 @@
     {
       id: 15,
       parentId: 7,
-      name: 'A715',
+      name: '[merged]',
       depth: 2,
       totalSize: 25,
       selfSize: 0,
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index f4f4d16..383f603 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -66,6 +66,7 @@
     copiedCallsite.parentId =
         getCallsitesParentHash(copiedCallsite, mergedCallsites);
 
+    let mergedAny = false;
     // If current callsite is small, find other small callsites with same depth
     // and parent and merge them into the current one, marking them as merged.
     if (copiedCallsite.totalSize <= minSizeDisplayed && i + 1 < data.length) {
@@ -77,10 +78,14 @@
             nextCallsite.totalSize <= minSizeDisplayed) {
           copiedCallsite.totalSize += nextCallsite.totalSize;
           mergedCallsites.set(nextCallsite.id, copiedCallsite.id);
+          mergedAny = true;
         }
         j++;
         nextCallsite = data[j];
       }
+      if (mergedAny) {
+        copiedCallsite.name = '[merged]';
+      }
     }
     mergedData.push(copiedCallsite);
   }
@@ -112,4 +117,4 @@
     i++;
   }
   return totalSize;
-}
\ No newline at end of file
+}
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index cf9fc43..b7f2f40 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -163,6 +163,7 @@
   id: number;
   upid: number;
   ts: number;
+  type: string;
 }
 
 export interface HeapProfileFlamegraph {
@@ -170,6 +171,7 @@
   id: number;
   upid: number;
   ts: number;
+  type: string;
   expandedCallsite?: CallsiteInfo;
   viewingOption?: string;
 }
@@ -424,6 +426,214 @@
   ];
 }
 
+export function getBuiltinChromeCategoryList(): string[] {
+  // List of static Chrome categories, last updated at Chromium 81.0.4021.0 from
+  // Chromium's //base/trace_event/builtin_categories.h.
+  return [
+    'accessibility',
+    'AccountFetcherService',
+    'android_webview',
+    'audio',
+    'base',
+    'benchmark',
+    'blink',
+    'blink.animations',
+    'blink.console',
+    'blink_gc',
+    'blink.net',
+    'blink_style',
+    'blink.user_timing',
+    'blink.worker',
+    'Blob',
+    'browser',
+    'browsing_data',
+    'CacheStorage',
+    'camera',
+    'cast_perf_test',
+    'cast.stream',
+    'cc',
+    'cc.debug',
+    'cdp.perf',
+    'chromeos',
+    'cma',
+    'compositor',
+    'content',
+    'content_capture',
+    'devtools',
+    'devtools.timeline',
+    'devtools.timeline.async',
+    'download',
+    'download_service',
+    'drm',
+    'drmcursor',
+    'dwrite',
+    'DXVA Decoding',
+    'EarlyJava',
+    'evdev',
+    'event',
+    'exo',
+    'explore_sites',
+    'FileSystem',
+    'file_system_provider',
+    'fonts',
+    'GAMEPAD',
+    'gpu',
+    'gpu.capture',
+    'headless',
+    'hwoverlays',
+    'identity',
+    'IndexedDB',
+    'input',
+    'io',
+    'ipc',
+    'Java',
+    'jni',
+    'jpeg',
+    'latency',
+    'latencyInfo',
+    'leveldb',
+    'loading',
+    'log',
+    'login',
+    'media',
+    'media_router',
+    'memory',
+    'midi',
+    'mojom',
+    'mus',
+    'native',
+    'navigation',
+    'net',
+    'netlog',
+    'offline_pages',
+    'omnibox',
+    'oobe',
+    'ozone',
+    'passwords',
+    'p2p',
+    'page-serialization',
+    'pepper',
+    'ppapi',
+    'ppapi proxy',
+    'rail',
+    'renderer',
+    'renderer_host',
+    'renderer.scheduler',
+    'RLZ',
+    'safe_browsing',
+    'screenlock_monitor',
+    'sequence_manager',
+    'service_manager',
+    'ServiceWorker',
+    'shell',
+    'shortcut_viewer',
+    'shutdown',
+    'SiteEngagement',
+    'skia',
+    'startup',
+    'sync',
+    'sync_lock_contention',
+    'thread_pool',
+    'test_gpu',
+    'test_tracing',
+    'toplevel',
+    'ui',
+    'v8',
+    'v8.execute',
+    'ValueStoreFrontend::Backend',
+    'views',
+    'views.frame',
+    'viz',
+    'vk',
+    'wayland',
+    'webaudio',
+    'weblayer',
+    'WebCore',
+    'webrtc',
+    'xr',
+    'disabled-by-default-animation-worklet',
+    'disabled-by-default-audio-worklet',
+    'disabled-by-default-blink.debug',
+    'disabled-by-default-blink.debug.display_lock',
+    'disabled-by-default-blink.debug.layout',
+    'disabled-by-default-blink.debug.layout.trees',
+    'disabled-by-default-blink.feature_usage',
+    'disabled-by-default-blink_gc',
+    'disabled-by-default-blink.image_decoding',
+    'disabled-by-default-blink.invalidation',
+    'disabled-by-default-cc',
+    'disabled-by-default-cc.debug',
+    'disabled-by-default-cc.debug.cdp-perf',
+    'disabled-by-default-cc.debug.display_items',
+    'disabled-by-default-cc.debug.picture',
+    'disabled-by-default-cc.debug.scheduler',
+    'disabled-by-default-cc.debug.scheduler.frames',
+    'disabled-by-default-cc.debug.scheduler.now',
+    'disabled-by-default-cpu_profiler',
+    'disabled-by-default-cpu_profiler.debug',
+    'disabled-by-default-devtools.screenshot',
+    'disabled-by-default-devtools.timeline',
+    'disabled-by-default-devtools.timeline.frame',
+    'disabled-by-default-devtools.timeline.inputs',
+    'disabled-by-default-devtools.timeline.invalidationTracking',
+    'disabled-by-default-devtools.timeline.layers',
+    'disabled-by-default-devtools.timeline.picture',
+    'disabled-by-default-file',
+    'disabled-by-default-fonts',
+    'disabled-by-default-gpu_cmd_queue',
+    'disabled-by-default-gpu.dawn',
+    'disabled-by-default-gpu.debug',
+    'disabled-by-default-gpu_decoder',
+    'disabled-by-default-gpu.device',
+    'disabled-by-default-gpu.service',
+    'disabled-by-default-histogram_samples',
+    'disabled-by-default-ipc.flow',
+    'disabled-by-default-java-heap-profiler',
+    'disabled-by-default-layer-element',
+    'disabled-by-default-lifecycles',
+    'disabled-by-default-loading',
+    'disabled-by-default-memory-infra',
+    'disabled-by-default-memory-infra.v8.code_stats',
+    'disabled-by-default-net',
+    'disabled-by-default-network',
+    'disabled-by-default-paint-worklet',
+    'disabled-by-default-power',
+    'disabled-by-default-renderer.scheduler',
+    'disabled-by-default-renderer.scheduler.debug',
+    'disabled-by-default-sequence_manager',
+    'disabled-by-default-sequence_manager.debug',
+    'disabled-by-default-sequence_manager.verbose_snapshots',
+    'disabled-by-default-skia',
+    'disabled-by-default-skia.gpu',
+    'disabled-by-default-skia.gpu.cache',
+    'disabled-by-default-SyncFileSystem',
+    'disabled-by-default-system_stats',
+    'disabled-by-default-thread_pool_diagnostics',
+    'disabled-by-default-toplevel.flow',
+    'disabled-by-default-toplevel.ipc',
+    'disabled-by-default-v8.compile',
+    'disabled-by-default-v8.cpu_profiler',
+    'disabled-by-default-v8.cpu_profiler.hires',
+    'disabled-by-default-v8.gc',
+    'disabled-by-default-v8.gc_stats',
+    'disabled-by-default-v8.ic_stats',
+    'disabled-by-default-v8.runtime',
+    'disabled-by-default-v8.runtime_stats',
+    'disabled-by-default-v8.runtime_stats_sampling',
+    'disabled-by-default-v8.turbofan',
+    'disabled-by-default-v8.wasm',
+    'disabled-by-default-video_and_image_capture',
+    'disabled-by-default-viz.debug.overlay_planes',
+    'disabled-by-default-viz.hit_testing_flow',
+    'disabled-by-default-viz.overdraw',
+    'disabled-by-default-viz.quads',
+    'disabled-by-default-viz.surface_id_flow',
+    'disabled-by-default-viz.surface_lifetime',
+    'disabled-by-default-viz.triangles',
+    'disabled-by-default-worker.scheduler',
+  ];
+}
+
 export function createEmptyState(): State {
   return {
     route: null,
diff --git a/ui/src/controller/adb.ts b/ui/src/controller/adb.ts
index 4888431..7b3c580 100644
--- a/ui/src/controller/adb.ts
+++ b/ui/src/controller/adb.ts
@@ -82,6 +82,9 @@
   };
 
   async findDevice() {
+    if (!('usb' in navigator)) {
+      throw new Error('WebUSB not supported by the browser (requires HTTPS)');
+    }
     return navigator.usb.requestDevice({filters: [this.filter]});
   }
 
diff --git a/ui/src/controller/adb_base_controller.ts b/ui/src/controller/adb_base_controller.ts
index 7f1afb1..31eb5ed 100644
--- a/ui/src/controller/adb_base_controller.ts
+++ b/ui/src/controller/adb_base_controller.ts
@@ -112,6 +112,7 @@
   }
 
   async findDevice(): Promise<USBDevice|undefined> {
+    if (!('usb' in navigator)) return undefined;
     const connectedDevice = globals.state.recordingTarget;
     if (!isAdbTarget(connectedDevice)) return undefined;
     const devices = await navigator.usb.getDevices();
diff --git a/ui/src/controller/heap_profile_controller.ts b/ui/src/controller/heap_profile_controller.ts
index 973a92f..2f3b07d 100644
--- a/ui/src/controller/heap_profile_controller.ts
+++ b/ui/src/controller/heap_profile_controller.ts
@@ -85,7 +85,8 @@
                           selectedHeapProfile.viewingOption :
                           DEFAULT_VIEWING_OPTION,
                       selection.ts,
-                      selectedHeapProfile.upid)
+                      selectedHeapProfile.upid,
+                      selectedHeapProfile.type)
                   .then(flamegraphData => {
                     if (flamegraphData !== undefined && selection &&
                         selection.kind === selectedHeapProfile.kind &&
@@ -119,6 +120,7 @@
       id: heapProfile.id,
       upid: heapProfile.upid,
       ts: heapProfile.ts,
+      type: heapProfile.type,
       expandedCallsite: heapProfile.expandedCallsite,
       viewingOption: heapProfile.viewingOption
     };
@@ -130,6 +132,7 @@
          (this.lastSelectedHeapProfile !== undefined &&
           (this.lastSelectedHeapProfile.id !== selection.id ||
            this.lastSelectedHeapProfile.ts !== selection.ts ||
+           this.lastSelectedHeapProfile.type !== selection.type ||
            this.lastSelectedHeapProfile.upid !== selection.upid ||
            this.lastSelectedHeapProfile.viewingOption !==
                selection.viewingOption ||
@@ -151,8 +154,8 @@
 
 
   async getFlamegraphData(
-      baseKey: string, viewingOption: string, ts: number,
-      upid: number): Promise<CallsiteInfo[]> {
+      baseKey: string, viewingOption: string, ts: number, upid: number,
+      type: string): Promise<CallsiteInfo[]> {
     let currentData: CallsiteInfo[];
     const key = `${baseKey}-${viewingOption}`;
     if (this.flamegraphDatasets.has(key)) {
@@ -163,7 +166,7 @@
       // Collecting data for drawing flamegraph for selected heap profile.
       // Data needs to be in following format:
       // id, name, parent_id, depth, total_size
-      const tableName = await this.prepareViewsAndTables(ts, upid);
+      const tableName = await this.prepareViewsAndTables(ts, upid, type);
       currentData =
           await this.getFlamegraphDataFromTables(tableName, viewingOption);
       this.flamegraphDatasets.set(key, currentData);
@@ -175,25 +178,32 @@
       tableName: string, viewingOption = DEFAULT_VIEWING_OPTION) {
     let orderBy = '';
     let sizeIndex = 4;
+    // TODO(fmayer): Improve performance so this is no longer necessary.
+    // Alternatively consider collapsing frames of the same label.
+    const maxDepth = 100;
     switch (viewingOption) {
       case SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY:
-        orderBy = `where size > 0 order by depth, parent_hash, size desc, name`;
+        orderBy = `where cumulative_size > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
+            cumulative_size desc, name`;
         sizeIndex = 4;
         break;
       case ALLOC_SPACE_MEMORY_ALLOCATED_KEY:
-        orderBy =
-            `where alloc_size > 0 order by depth, parent_hash, alloc_size desc,
-            name`;
+        orderBy = `where cumulative_alloc_size > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
+            cumulative_alloc_size desc, name`;
         sizeIndex = 5;
         break;
       case OBJECTS_ALLOCATED_NOT_FREED_KEY:
-        orderBy =
-            `where count > 0 order by depth, parent_hash, count desc, name`;
+        orderBy = `where cumulative_count > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
+            cumulative_count desc, name`;
         sizeIndex = 6;
         break;
       case OBJECTS_ALLOCATED_KEY:
-        orderBy = `where alloc_count > 0 order by depth, parent_hash,
-            alloc_count desc, name`;
+        orderBy = `where cumulative_alloc_count > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
+            cumulative_alloc_count desc, name`;
         sizeIndex = 7;
         break;
       default:
@@ -201,14 +211,15 @@
     }
 
     const callsites = await this.args.engine.query(
-        `SELECT hash, name, parent_hash, depth, size, alloc_size, count,
-        alloc_count, map_name, self_size from ${tableName} ${orderBy}`);
+        `SELECT id, IFNULL(DEMANGLE(name), name), IFNULL(parent_id, -1), depth,
+        cumulative_size, cumulative_alloc_size, cumulative_count,
+        cumulative_alloc_count, map_name, size from ${tableName} ${orderBy}`);
 
     const flamegraphData: CallsiteInfo[] = new Array();
     const hashToindex: Map<number, number> = new Map();
     for (let i = 0; i < callsites.numRecords; i++) {
       const hash = callsites.columns[0].longValues![i];
-      const name = callsites.columns[1].stringValues![i];
+      let name = callsites.columns[1].stringValues![i];
       const parentHash = callsites.columns[2].longValues![i];
       const depth = +callsites.columns[3].longValues![i];
       const totalSize = +callsites.columns[sizeIndex].longValues![i];
@@ -216,6 +227,9 @@
       const selfSize = +callsites.columns[9].longValues![i];
       const parentId =
           hashToindex.has(+parentHash) ? hashToindex.get(+parentHash)! : -1;
+      if (depth === maxDepth - 1) {
+        name += ' [tree truncated]';
+      }
       hashToindex.set(+hash, i);
       // Instead of hash, we will store index of callsite in this original array
       // as an id of callsite. That way, we have quicker access to parent and it
@@ -226,106 +240,19 @@
     return flamegraphData;
   }
 
-  private async prepareViewsAndTables(ts: number, upid: number):
+  private async prepareViewsAndTables(ts: number, upid: number, type: string):
       Promise<string> {
     // Creating unique names for views so we can reuse and not delete them
     // for each marker.
-    const tableNameCallsiteNameSize =
-        this.tableName(`callsite_with_name_and_size`);
-    const tableNameCallsiteHashNameSize =
-        this.tableName(`callsite_hash_name_size`);
     const tableNameGroupedCallsitesForFlamegraph =
         this.tableName(`grouped_callsites_for_flamegraph`);
-    // Joining the callsite table with frame table then with alloc table to get
-    // the size and name for each callsite.
-    // TODO(taylori): Make frame name nullable in the trace processor for
-    // consistency with the other columns.
-    await this.args.engine.query(
-        `create view if not exists ${tableNameCallsiteNameSize} as
-         select id, parent_id, depth, IFNULL(DEMANGLE(name), name) as name,
-            map_name, size, alloc_size, count, alloc_count from (
-         select cs.id as id, parent_id, depth,
-            coalesce(symbols.name,
-                case when fr.name != '' then fr.name else map.name end) as name,
-            map.name as map_name,
-            SUM(IFNULL(size, 0)) as size,
-            SUM(IFNULL(size, 0)) as size,
-            SUM(case when size > 0 then size else 0 end) as alloc_size,
-            SUM(IFNULL(count, 0)) as count,
-            SUM(case when count > 0 then count else 0 end) as alloc_count
-         from stack_profile_callsite cs
-         join stack_profile_frame fr on cs.frame_id = fr.id
-         join stack_profile_mapping map on fr.mapping = map.id
-         left join (
-              select symbol_set_id, FIRST_VALUE(name) OVER(PARTITION BY
-                symbol_set_id) as name
-              from stack_profile_symbol GROUP BY symbol_set_id
-            ) as symbols using(symbol_set_id)
-         left join heap_profile_allocation alloc on alloc.callsite_id = cs.id
-         and alloc.ts <= ${ts} and alloc.upid = ${upid} group by cs.id)`);
 
-    // Recursive query to compute the hash for each callsite based on names
-    // rather than ids.
-    // We get all the children of the row in question and emit a row with hash
-    // equal hash(name, parent.hash). Roots without the parent will have -1 as
-    // hash.  Slices will be merged into a big slice.
-    await this.args.engine.query(
-        `create view if not exists ${tableNameCallsiteHashNameSize} as
-        with recursive callsite_table_names(
-          id, hash, name, map_name, size, alloc_size, count, alloc_count,
-          parent_hash, depth) AS (
-        select id, hash(name) as hash, name, map_name, size, alloc_size, count,
-          alloc_count, -1, depth
-        from ${tableNameCallsiteNameSize}
-        where depth = 0
-        union all
-        select cs.id, hash(cs.name, ctn.hash) as hash, cs.name, cs.map_name,
-          cs.size, cs.alloc_size, cs.count, cs.alloc_count, ctn.hash, cs.depth
-        from callsite_table_names ctn
-        inner join ${tableNameCallsiteNameSize} cs ON ctn.id = cs.parent_id
-        )
-        select hash, name, map_name, parent_hash, depth, SUM(size) as size,
-          SUM(case when alloc_size > 0 then alloc_size else 0 end)
-            as alloc_size, SUM(count) as count,
-          SUM(case when alloc_count > 0 then alloc_count else 0 end)
-            as alloc_count
-        from callsite_table_names
-        group by hash`);
-
-    // Recursive query to compute the cumulative size of each callsite.
-    // Base case: We get all the callsites where the size is non-zero.
-    // Recursive case: We get the callsite which is the parent of the current
-    //  callsite(in terms of hashes) and emit a row with the size of the current
-    //  callsite plus all the info of the parent.
-    // Grouping: For each callsite, our recursive table has n rows where n is
-    //  the number of descendents with a non-zero self size. We need to group on
-    //  the hash and sum all the sizes to get the cumulative size for each
-    //  callsite hash.
     await this.args.engine.query(`create temp table if not exists ${
-        tableNameGroupedCallsitesForFlamegraph}
-        as with recursive callsite_children(
-          hash, name, map_name, parent_hash, depth, size, alloc_size, count,
-          alloc_count, self_size, self_alloc_size, self_count, self_alloc_count)
-        as (
-        select hash, name, map_name, parent_hash, depth, size, alloc_size,
-          count, alloc_count, size as self_size, alloc_size as self_alloc_size,
-          count as self_count, alloc_count as self_alloc_count
-        from ${tableNameCallsiteHashNameSize}
-        union all
-        select chns.hash, chns.name, chns.map_name, chns.parent_hash,
-          chns.depth, cc.size, cc.alloc_size, cc.count, cc.alloc_count,
-          chns.size, chns.alloc_size, chns.count, chns.alloc_count
-        from ${tableNameCallsiteHashNameSize} chns
-        inner join callsite_children cc on chns.hash = cc.parent_hash
-        )
-        select hash, name, map_name, parent_hash, depth, SUM(size) as size,
-          SUM(case when alloc_size > 0 then alloc_size else 0 end)
-            as alloc_size, SUM(count) as count,
-          SUM(case when alloc_count > 0 then alloc_count else 0 end) as
-            alloc_count,
-          self_size, self_alloc_size, self_count, self_alloc_count
-        from callsite_children
-        group by hash`);
+        tableNameGroupedCallsitesForFlamegraph} as
+        select id, name, map_name, parent_id, depth, cumulative_size,
+          cumulative_alloc_size, cumulative_count, cumulative_alloc_count,
+          size, alloc_size, count, alloc_count
+        from experimental_flamegraph(${ts}, ${upid}, '${type}')`);
     return tableNameGroupedCallsitesForFlamegraph;
   }
 
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 88e2190..76acaa9 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -374,10 +374,14 @@
 
     const upidToProcessTracks = new Map();
     const rawProcessTracks = await engine.query(`
-      select id, upid, process_track.name, max(depth) as maxDepth
+      select
+        process_track.id as track_id,
+        process_track.upid,
+        process_track.name,
+        max(slice.depth) as max_depth
       from process_track
-      inner join slice on slice.track_id = process_track.id
-      group by track_id
+      join slice on slice.track_id = process_track.id
+      group by process_track.id
     `);
     for (let i = 0; i < rawProcessTracks.numRecords; i++) {
       const trackId = rawProcessTracks.columns[0].longValues![i];
@@ -403,7 +407,9 @@
     }
 
     const heapProfiles = await engine.query(`
-      select distinct(upid) from heap_profile_allocation`);
+      select distinct(upid) from heap_profile_allocation
+      union
+      select distinct(upid) from heap_graph_object`);
 
     const heapUpids: Set<number> = new Set();
     for (let i = 0; i < heapProfiles.numRecords; i++) {
@@ -467,6 +473,32 @@
       });
     }
 
+    // Add global slice tracks.
+    const globalSliceTracks = await engine.query(`
+      select
+        track.name as track_name,
+        track.id as track_id,
+        max(depth) as max_depth
+      from track
+      join slice on track.id = slice.track_id
+      where track.type = 'track'
+      group by track_id
+    `);
+
+    for (let i = 0; i < globalSliceTracks.numRecords; i++) {
+      const name = globalSliceTracks.columns[0].stringValues![i];
+      const trackId = +globalSliceTracks.columns[1].longValues![i];
+      const maxDepth = +globalSliceTracks.columns[2].longValues![i];
+
+      tracksToAdd.push({
+        engineId: this.engineId,
+        kind: SLICE_TRACK_KIND,
+        name: `${name}`,
+        trackGroup: SCROLLING_TRACK_GROUP,
+        config: {maxDepth, trackId},
+      });
+    }
+
     interface CounterTrack {
       name: string;
       trackId: number;
@@ -687,8 +719,6 @@
           name: `${threadName} [${tid}]`,
           trackGroup: pUuid,
           config: {
-            upid,
-            utid,
             maxDepth: threadTrack.maxDepth,
             trackId: threadTrack.trackId
           },
@@ -839,9 +869,9 @@
       select first_thread.ts as ts,
       coalesce(min(runnable.ts), (select end_ts from trace_bounds)) -
       first_thread.ts as dur,
-      runnable.utid as utid
-      from runnable
-      JOIN first_thread using(utid) group by utid`);
+      first_thread.utid as utid
+      from first_thread
+      LEFT JOIN runnable using(utid) group by utid`);
 
     await engine.query(`create view full_runnable as
         select * from runnable UNION
diff --git a/ui/src/frontend/colorizer.ts b/ui/src/frontend/colorizer.ts
index 5fa6681..f4c5780 100644
--- a/ui/src/frontend/colorizer.ts
+++ b/ui/src/frontend/colorizer.ts
@@ -63,21 +63,40 @@
   return (128 + (32 * cpu)) % 256;
 }
 
-export function colorForState(state: string): Color {
+const DARK_GREEN: Color = {
+  c: 'dark green',
+  h: 120,
+  s: 44,
+  l: 34
+};
+const LIME_GREEN: Color = {
+  c: 'lime green',
+  h: 75,
+  s: 55,
+  l: 47
+};
+const LIGHT_GREY: Color = {
+  c: 'light grey',
+  h: 0,
+  s: 0,
+  l: 87
+};
+
+export function colorForState(state: string): Readonly<Color> {
   switch (state) {
     case 'Running':
     case 'Busy':
-      return {c: 'dark green', h: 120, s: 44, l: 34};
+      return DARK_GREEN;
     case 'Runnable':
     case 'R':
     case 'R+':
-      return {c: 'lime green', h: 75, s: 55, l: 47};
+      return LIME_GREEN;
     default:
-      return {c: 'light grey', h: 0, s: 0, l: 87};
+      return LIGHT_GREY;
   }
 }
 
-export function colorForTid(tid: number) {
+export function colorForTid(tid: number): Color {
   const colorIdx = hash(tid.toString(), MD_PALETTE.length);
   return Object.assign({}, MD_PALETTE[colorIdx]);
 }
diff --git a/ui/src/frontend/flamegraph.ts b/ui/src/frontend/flamegraph.ts
index 4c4d3e4..e74befe 100644
--- a/ui/src/frontend/flamegraph.ts
+++ b/ui/src/frontend/flamegraph.ts
@@ -259,11 +259,13 @@
 
       let selfSizeWidth = 0;
       if (this.hoveredCallsite.selfSize > 0) {
+        const selfPercentage =
+            this.hoveredCallsite.selfSize / this.totalSize * 100;
         const selfSizeText = `self: ${
             this.displaySize(
                 this.hoveredCallsite.selfSize,
                 unit,
-                unit === 'B' ? 1024 : 1000)} (${percentage.toFixed(2)}%)`;
+                unit === 'B' ? 1024 : 1000)} (${selfPercentage.toFixed(2)}%)`;
         lineSplitter = splitIfTooBig(
             selfSizeText, width, ctx.measureText(selfSizeText).width);
         selfSizeWidth = lineSplitter.lineWidth;
@@ -305,15 +307,18 @@
     if (unit === '') return totalSize.toLocaleString();
     if (totalSize === 0) return `0 ${unit}`;
     const units = [
-      ['', 0],
+      ['', 1],
       ['K', step],
       ['M', Math.pow(step, 2)],
       ['G', Math.pow(step, 3)]
     ];
     let unitsIndex = Math.trunc(Math.log(totalSize) / Math.log(step));
     unitsIndex = unitsIndex > units.length - 1 ? units.length - 1 : unitsIndex;
-    return `${(totalSize / +units[unitsIndex][1]).toLocaleString()} ${
-        units[unitsIndex][0]}${unit}`;
+    const result = totalSize / +units[unitsIndex][1];
+    const resultString = totalSize % +units[unitsIndex][1] === 0 ?
+        result.toString() :
+        result.toFixed(2);
+    return `${resultString} ${units[unitsIndex][0]}${unit}`;
   }
 
   onMouseMove({x, y}: {x: number, y: number}) {
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index b38fe94..97acfd3 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -99,6 +99,7 @@
   showNotePreview = false;
   localOnlyMode = false;
   sidebarVisible = true;
+  showPanningHint = false;
   // This is used to calculate the tracks within a Y range for area selection.
   areaY: Range = {};
   visibleTracks = new Set<string>();
@@ -107,6 +108,7 @@
   currentTab?: Tab;
   scrollToTrackId?: string|number;
   httpRpcState: HttpRpcState = {connected: false};
+  newVersionAvailable = false;
   private scrollBarWidth?: number;
 
   private _omniboxState: OmniboxState = {
@@ -224,6 +226,7 @@
   selectArea(
       startSec: number, endSec: number,
       tracks = this._selectedArea.area ? this._selectedArea.area.tracks : []) {
+    this.showPanningHint = true;
     this._selectedArea = {
       area: {startSec, endSec, tracks},
       lastUpdate: Date.now() / 1000
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 0fb518d..7c384ba 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -20,6 +20,7 @@
 
 import {FrontendLocalState} from './frontend_local_state';
 import {RafScheduler} from './raf_scheduler';
+import {ServiceWorkerController} from './service_worker_controller';
 
 type Dispatch = (action: DeferredAction) => void;
 type TrackDataStore = Map<string, {}>;
@@ -85,6 +86,7 @@
   private _state?: State = undefined;
   private _frontendLocalState?: FrontendLocalState = undefined;
   private _rafScheduler?: RafScheduler = undefined;
+  private _serviceWorkerController?: ServiceWorkerController = undefined;
 
   // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
   private _trackDataStore?: TrackDataStore = undefined;
@@ -126,6 +128,7 @@
     this._state = createEmptyState();
     this._frontendLocalState = new FrontendLocalState();
     this._rafScheduler = new RafScheduler();
+    this._serviceWorkerController = new ServiceWorkerController();
 
     // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
     this._trackDataStore = new Map<string, {}>();
@@ -157,6 +160,10 @@
     return assertExists(this._rafScheduler);
   }
 
+  get serviceWorkerController() {
+    return assertExists(this._serviceWorkerController);
+  }
+
   // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
   get overviewStore(): OverviewStore {
     return assertExists(this._overviewStore);
@@ -262,6 +269,7 @@
     this._state = undefined;
     this._frontendLocalState = undefined;
     this._rafScheduler = undefined;
+    this._serviceWorkerController = undefined;
 
     // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
     this._trackDataStore = undefined;
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 39ff0e4..1227d47 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -35,41 +35,39 @@
   helpModelOpen = true;
   showModal({
     title: 'Perfetto Help',
-    content:
-        m('.help',
-          m('h2', 'Navigation'),
-          m(
-              'table',
-              m(
-                  'tr',
-                  m('td', keycap('w'), '/', keycap('s')),
-                  m('td', 'Zoom in/out'),
-                  ),
-              m(
-                  'tr',
-                  m('td', keycap('a'), '/', keycap('d')),
-                  m('td', 'Pan left/right'),
-                  ),
-              ),
-          m('h2', 'Mouse Controls'),
-          m('table',
-            m('tr', m('td', 'Click'), m('td', 'Select event')),
-            m('tr', m('td', 'Ctrl + Scroll wheel'), m('td', 'Zoom in/out')),
-            m('tr', m('td', 'Click + Drag'), m('td', 'Pan left/right')),
+    content: m(
+        '.help',
+        m('h2', 'Navigation'),
+        m(
+            'table',
+            m(
+                'tr',
+                m('td', keycap('w'), '/', keycap('s')),
+                m('td', 'Zoom in/out'),
+                ),
+            m(
+                'tr',
+                m('td', keycap('a'), '/', keycap('d')),
+                m('td', 'Pan left/right'),
+                ),
+            ),
+        m('h2', 'Mouse Controls'),
+        m('table',
+          m('tr', m('td', 'Click'), m('td', 'Select event')),
+          m('tr', m('td', 'Ctrl + Scroll wheel'), m('td', 'Zoom in/out')),
+          m('tr', m('td', 'Click + Drag'), m('td', 'Select area')),
+          m('tr', m('td', 'Shift + Click + Drag'), m('td', 'Pan left/right'))),
+        m('h2', 'Other'),
+        m(
+            'table',
             m('tr',
-              m('td', 'Shift + Click + Drag'),
-              m('td', 'Select a time span'))),
-          m('h2', 'Other'),
-          m(
-              'table',
-              m('tr',
-                m('td', keycap('f'), ' (with event selected)'),
-                m('td', 'Scroll + zoom to current selection')),
-              m('tr',
-                m('td', keycap('m'), ' (with event selected)'),
-                m('td', 'Select time span of event')),
-              m('tr', m('td', keycap('?')), m('td', 'Show help')),
-              )),
+              m('td', keycap('f'), ' (with event selected)'),
+              m('td', 'Scroll + zoom to current selection')),
+            m('tr',
+              m('td', keycap('m'), ' (with event selected)'),
+              m('td', 'Select time span of event')),
+            m('tr', m('td', keycap('?')), m('td', 'Show help')),
+            )),
     buttons: [],
   }).finally(() => {
     helpModelOpen = false;
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index c9f75bc..e1d5c64 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -78,7 +78,7 @@
   // want to keep in the global state. Figure out a more generic and type-safe
   // mechanism to achieve this.
 
-  publishOverviewData(data: {[key: string]: QuantizedLoad | QuantizedLoad[]}) {
+  publishOverviewData(data: {[key: string]: QuantizedLoad|QuantizedLoad[]}) {
     for (const [key, value] of Object.entries(data)) {
       if (!globals.overviewStore.has(key)) {
         globals.overviewStore.set(key, []);
@@ -251,6 +251,7 @@
       dispatch);
   forwardRemoteCalls(frontendChannel.port2, new FrontendApi(router));
   globals.initialize(dispatch, controller);
+  globals.serviceWorkerController.install();
 
   // We proxy messages between the extension and the controller because the
   // controller's worker can't access chrome.runtime.
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 3132234..642722e 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -46,6 +46,9 @@
       globals.frontendLocalState.setVidTimestamp(Number.MIN_SAFE_INTEGER);
     }
   }
+  if (down && 'b' === key && (e.ctrlKey || e.metaKey)) {
+    globals.frontendLocalState.toggleSidebar();
+  }
   if (down && '?' === key) {
     toggleHelp();
   }
diff --git a/ui/src/frontend/legacy_trace_viewer.ts b/ui/src/frontend/legacy_trace_viewer.ts
index c55451d..253d6af 100644
--- a/ui/src/frontend/legacy_trace_viewer.ts
+++ b/ui/src/frontend/legacy_trace_viewer.ts
@@ -18,12 +18,48 @@
 
 import {globals} from './globals';
 
-export function isLegacyTrace(fileName: string): boolean {
-  fileName = fileName.toLowerCase();
-  return (
-      fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
+function readText(blob: Blob): Promise<string> {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.onload = () => {
+      if (typeof reader.result === 'string') {
+        return resolve(reader.result);
+      }
+    };
+    reader.onerror = err => {
+      reject(err);
+    };
+    reader.readAsText(blob);
+  });
+}
+
+export async function isLegacyTrace(file: File): Promise<boolean> {
+  const fileName = file.name.toLowerCase();
+  if (fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
       fileName.endsWith('.zip') || fileName.endsWith('.ctrace') ||
-      fileName.endsWith('.html'));
+      fileName.endsWith('.html')) {
+    return true;
+  }
+
+  // Sometimes systrace formatted traces end with '.trace'. This is a
+  // little generic to assume all such traces are systrace format though
+  // so we read the beginning of the file and check to see if is has the
+  // systrace header (several comment lines):
+  if (fileName.endsWith('.trace')) {
+    const header = await readText(file.slice(0, 512));
+    const lines = header.split('\n');
+    let commentCount = 0;
+    for (const line of lines) {
+      if (line.startsWith('#')) {
+        commentCount++;
+      }
+    }
+    if (commentCount > 5) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 export function openFileWithLegacyTraceViewer(file: File) {
diff --git a/ui/src/frontend/pan_and_zoom_handler.ts b/ui/src/frontend/pan_and_zoom_handler.ts
index 9d2bda1..1f504b4 100644
--- a/ui/src/frontend/pan_and_zoom_handler.ts
+++ b/ui/src/frontend/pan_and_zoom_handler.ts
@@ -44,8 +44,8 @@
 const WHEEL_ZOOM_SPEED = -0.02;
 
 const EDITING_RANGE_CURSOR = 'ew-resize';
-const SHIFT_CURSOR = 'text';
-const DEFAULT_CURSOR = 'default';
+const DRAG_CURSOR = 'text';
+const PAN_CURSOR = 'move';
 
 enum Pan {
   None = 0,
@@ -94,28 +94,29 @@
   private contentOffsetX: number;
   private onPanned: (movedPx: number) => void;
   private onZoomed: (zoomPositionPx: number, zoomRatio: number) => void;
-  private shouldDrag: (currentPx: number) => boolean;
-  private onDrag:
+  private editSelection: (currentPx: number) => boolean;
+  private onSelection:
       (dragStartX: number, dragStartY: number, prevX: number, currentX: number,
        currentY: number, editing: boolean) => void;
 
   constructor(
-      {element, contentOffsetX, onPanned, onZoomed, shouldDrag, onDrag}: {
-        element: HTMLElement,
-        contentOffsetX: number,
-        onPanned: (movedPx: number) => void,
-        onZoomed: (zoomPositionPx: number, zoomRatio: number) => void,
-        shouldDrag: (currentPx: number) => boolean,
-        onDrag:
-            (dragStartX: number, dragStartY: number, prevX: number,
-             currentX: number, currentY: number, editing: boolean) => void,
-      }) {
+      {element, contentOffsetX, onPanned, onZoomed, editSelection, onSelection}:
+          {
+            element: HTMLElement,
+            contentOffsetX: number,
+            onPanned: (movedPx: number) => void,
+            onZoomed: (zoomPositionPx: number, zoomRatio: number) => void,
+            editSelection: (currentPx: number) => boolean,
+            onSelection:
+                (dragStartX: number, dragStartY: number, prevX: number,
+                 currentX: number, currentY: number, editing: boolean) => void,
+          }) {
     this.element = element;
     this.contentOffsetX = contentOffsetX;
     this.onPanned = onPanned;
     this.onZoomed = onZoomed;
-    this.shouldDrag = shouldDrag;
-    this.onDrag = onDrag;
+    this.editSelection = editSelection;
+    this.onSelection = onSelection;
 
     document.body.addEventListener('keydown', this.boundOnKeyDown);
     document.body.addEventListener('keyup', this.boundOnKeyUp);
@@ -125,16 +126,14 @@
     let prevX = -1;
     let dragStartX = -1;
     let dragStartY = -1;
-    let drag = false;
+    let edit = false;
     new DragGestureHandler(
         this.element,
         (x, y) => {
-          // If we started our drag on a time range boundary or shift is down
-          // then we are drag selecting rather than panning.
-          if (drag || this.shiftDown) {
-            this.onDrag(dragStartX, dragStartY, prevX, x, y, !this.shiftDown);
-          } else {
+          if (this.shiftDown) {
             this.onPanned(prevX - x);
+          } else {
+            this.onSelection(dragStartX, dragStartY, prevX, x, y, edit);
           }
           prevX = x;
         },
@@ -142,19 +141,18 @@
           prevX = x;
           dragStartX = x;
           dragStartY = y;
-          drag = this.shouldDrag(x);
+          edit = this.editSelection(x);
           // Set the cursor style based on where the cursor is when the drag
           // starts.
-          if (drag) {
+          if (edit) {
             this.element.style.cursor = EDITING_RANGE_CURSOR;
-          } else if (this.shiftDown) {
-            this.element.style.cursor = SHIFT_CURSOR;
+          } else if (!this.shiftDown) {
+            this.element.style.cursor = DRAG_CURSOR;
           }
         },
         () => {
           // Reset the cursor now the drag has ended.
-          this.element.style.cursor =
-              this.shiftDown ? SHIFT_CURSOR : DEFAULT_CURSOR;
+          this.element.style.cursor = this.shiftDown ? PAN_CURSOR : DRAG_CURSOR;
           dragStartX = -1;
           dragStartY = -1;
         });
@@ -213,14 +211,13 @@
     // the cursor flickering between styles if you drag fast and get too
     // far from the current time range.
     if (e.buttons === 0) {
-      if (!this.shouldDrag(this.mousePositionX)) {
-        this.element.style.cursor =
-            this.shiftDown ? SHIFT_CURSOR : DEFAULT_CURSOR;
-      } else {
+      if (this.editSelection(this.mousePositionX)) {
         this.element.style.cursor = EDITING_RANGE_CURSOR;
+      } else {
+        this.element.style.cursor = this.shiftDown ? PAN_CURSOR : DRAG_CURSOR;
       }
     }
-    if (this.shiftDown) {
+    if (!this.shiftDown) {
       const pos = this.mousePositionX - TRACK_SHELL_WIDTH;
       const ts = globals.frontendLocalState.timeScale.pxToTime(pos);
       globals.frontendLocalState.setHoveredTimestamp(ts);
@@ -283,17 +280,15 @@
     if (down === this.shiftDown) return;
     this.shiftDown = down;
     if (this.shiftDown) {
+      globals.frontendLocalState.setHoveredTimestamp(-1);
+      this.element.style.cursor = PAN_CURSOR;
+    } else {
       if (this.mousePositionX) {
-        this.element.style.cursor = SHIFT_CURSOR;
+        this.element.style.cursor = DRAG_CURSOR;
         const pos = this.mousePositionX - TRACK_SHELL_WIDTH;
         const ts = globals.frontendLocalState.timeScale.pxToTime(pos);
         globals.frontendLocalState.setHoveredTimestamp(ts);
       }
-    } else {
-      globals.frontendLocalState.setHoveredTimestamp(-1);
-      this.element.style.cursor = DEFAULT_CURSOR;
     }
-
-    globals.frontendLocalState.setShowTimeSelectPreview(this.shiftDown);
   }
 }
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 9d2eaa5..8698422 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -234,13 +234,18 @@
     const ctx = assertExists(this.ctx);
     const canvas = assertExists(ctx.canvas);
     canvas.style.height = `${this.canvasHeight}px`;
+
+    // If're we're non-scrolling canvas and the scroll-limiter should always
+    // have the same height. Enforce this by explicitly setting the height.
+    if (!this.attrs.doesScroll) {
+      const scrollLimiter = canvas.parentElement;
+      if (scrollLimiter) {
+        scrollLimiter.style.height = `${this.canvasHeight}px`;
+      }
+    }
+
     const dpr = window.devicePixelRatio;
-    // On non-MacOS if there is a solid scroll bar it can cover important
-    // pixels, reduce the size of the canvas so it doesn't overlap with
-    // the scroll bar.
-    ctx.canvas.width =
-        (this.parentWidth - globals.frontendLocalState.getScrollbarWidth()) *
-        dpr;
+    ctx.canvas.width = this.parentWidth * dpr;
     ctx.canvas.height = this.canvasHeight * dpr;
     ctx.scale(dpr, dpr);
   }
@@ -260,7 +265,11 @@
     const oldWidth = this.parentWidth;
     const oldHeight = this.parentHeight;
     const clientRect = assertExists(dom.parentElement).getBoundingClientRect();
-    this.parentWidth = clientRect.width;
+    // On non-MacOS if there is a solid scroll bar it can cover important
+    // pixels, reduce the size of the canvas so it doesn't overlap with
+    // the scroll bar.
+    this.parentWidth =
+        clientRect.width - globals.frontendLocalState.getScrollbarWidth();
     this.parentHeight = clientRect.height;
     return this.parentHeight !== oldHeight || this.parentWidth !== oldWidth;
   }
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 135c874..a1d1b54 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -20,11 +20,11 @@
 import {MeminfoCounters, VmstatCounters} from '../common/protos';
 import {
   AdbRecordingTarget,
+  getBuiltinChromeCategoryList,
   getDefaultRecordingTargets,
   isAdbTarget,
   isAndroidTarget,
   isChromeTarget,
-  isLinuxTarget,
   RecordingTarget
 } from '../common/state';
 import {MAX_TIME, RecordMode} from '../common/state';
@@ -592,11 +592,13 @@
 }
 
 function ChromeCategoriesSelection() {
-  // The categories are displayed only if the extension is installed, because
-  // they come from the chrome.debugging API, not available from normal web
-  // pages.
-  const categories = globals.state.chromeCategories;
-  if (!categories) return [];
+  // If we are attempting to record via the Chrome extension, we receive the
+  // list of actually supported categories via DevTools. Otherwise, we fall back
+  // to an integrated list of categories from a recent version of Chrome.
+  let categories = globals.state.chromeCategories;
+  if (!categories || !isChromeTarget(globals.state.recordingTarget)) {
+    categories = getBuiltinChromeCategoryList();
+  }
 
   // Show "disabled-by-default" categories last.
   const categoriesMap = new Map<string, string>();
@@ -613,9 +615,10 @@
     categoriesMap.set(
         cat, `${cat.replace(disabledPrefix, '')} (high overhead)`);
   });
+
   return m(Dropdown, {
     title: 'Additional Chrome categories',
-    cssClass: '.multicolumn.two-columns.chrome-categories',
+    cssClass: '.multicolumn.two-columns',
     options: categoriesMap,
     set: (cfg, val) => cfg.chromeCategoriesSelected = val,
     get: (cfg) => cfg.chromeCategoriesSelected
@@ -743,7 +746,7 @@
           ),
       m('.chip',
         {onclick: addAndroidDevice},
-        m('button', 'Add Device'),
+        m('button', 'Add ADB Device'),
         m('i.material-icons', 'add')));
 }
 
@@ -850,7 +853,7 @@
          'Start Recording'.`)) :
         [];
   }
-  return m(CodeSnippet, {text: getRecordCommand(target), hardWhitespace: true});
+  return m(CodeSnippet, {text: getRecordCommand(target)});
 }
 
 function getRecordCommand(target: RecordingTarget) {
@@ -895,29 +898,16 @@
           onclick: onStartRecordingPressed
         },
         'Start Recording');
-  const showCmd =
-      m(`button`,
-        {
-          onclick: () => {
-            location.href = '#!/record?p=instructions';
-            globals.rafScheduler.scheduleFullRedraw();
-          }
-        },
-        'Show Command');
 
   const buttons: m.Children = [];
 
   if (isAndroidTarget(target)) {
-    if (!recInProgress) {
-      buttons.push(showCmd);
-      if (isAdbTarget(target)) buttons.push(start);
+    if (!recInProgress && isAdbTarget(target)) {
+      buttons.push(start);
     }
   } else if (isChromeTarget(target) && state.extensionInstalled) {
     buttons.push(start);
-  } else if (isLinuxTarget(target)) {
-    buttons.push(showCmd);
   }
-
   return m('.button', buttons);
 }
 
@@ -973,7 +963,9 @@
   try {
     device = await new AdbOverWebUsb().findDevice();
   } catch (e) {
-    console.error(`No device found: ${e.name}: ${e.message}`);
+    const err = `No device found: ${e.name}: ${e.message}`;
+    console.error(err, e);
+    alert(err);
     return;
   }
 
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/frontend/record_widgets.ts
index 3d683f7..ebc2267 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/frontend/record_widgets.ts
@@ -258,12 +258,7 @@
             onclick: () => copyToClipboard(attrs.text),
           },
           m('i.material-icons', 'assignment')),
-        m('code',
-          {
-            style: {
-              'white-space': attrs.hardWhitespace ? 'pre' : null,
-            },
-          },
-          attrs.text), );
+        m('code', attrs.text),
+    );
   }
 }
diff --git a/ui/src/frontend/service_worker_controller.ts b/ui/src/frontend/service_worker_controller.ts
new file mode 100644
index 0000000..efffa21
--- /dev/null
+++ b/ui/src/frontend/service_worker_controller.ts
@@ -0,0 +1,120 @@
+// Copyright (C) 2020 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.
+
+// Handles registration, unregistration and lifecycle of the service worker.
+// This class contains only the controlling logic, all the code in here runs in
+// the main thread, not in the service worker thread.
+// The actual service worker code is in src/service_worker.
+// Design doc: http://go/perfetto-offline.
+
+import {reportError} from '../base/logging';
+import {globals} from './globals';
+
+// We use a dedicated |caches| object to share a global boolean beween the main
+// thread and the SW. SW cannot use local-storage or anything else other than
+// IndexedDB (which would be overkill).
+const BYPASS_ID = 'BYPASS_SERVICE_WORKER';
+
+export class ServiceWorkerController {
+  private _initialWorker: ServiceWorker|null = null;
+  private _bypassed = false;
+  private _installing = false;
+
+  // Caller should reload().
+  async setBypass(bypass: boolean) {
+    if (!('serviceWorker' in navigator)) return;  // Not supported.
+    this._bypassed = bypass;
+    if (bypass) {
+      await caches.open(BYPASS_ID);  // Create the entry.
+      for (const reg of await navigator.serviceWorker.getRegistrations()) {
+        await reg.unregister();
+      }
+    } else {
+      await caches.delete(BYPASS_ID);
+      this.install();
+    }
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  onStateChange(sw: ServiceWorker) {
+    globals.rafScheduler.scheduleFullRedraw();
+    if (sw.state === 'installing') {
+      this._installing = true;
+    } else if (sw.state === 'activated') {
+      this._installing = false;
+      // Don't show the notification if the site was served straight
+      // from the network (e.g., on the very first visit or after
+      // Ctrl+Shift+R). In these cases, we are already at the last
+      // version.
+      if (sw !== this._initialWorker && this._initialWorker) {
+        globals.frontendLocalState.newVersionAvailable = true;
+      }
+    } else if (
+        sw.state === 'redundant' && sw !== this._initialWorker &&
+        !this._bypassed) {
+      // Note that upon updates, the initial SW will hit the 'redundant'
+      // state by design once the new one is activated. That's why the
+      // != _initialWorker above.
+
+      // In the other cases, the 'redundant' state signals a failure in the
+      // SW installation. This can happen, for instance, if the subresource
+      // integrity check fails. In that case there doesn't seem to be any easy
+      // way to get the failure output from the service worker.
+      reportError(
+          'Service Worker installation failed.\n' +
+          'Please attach the JavaScript console output to the bug.');
+    }
+  }
+
+  monitorWorker(sw: ServiceWorker|null) {
+    if (!sw) return;
+    sw.addEventListener('error', (e) => reportError(e));
+    sw.addEventListener('statechange', () => this.onStateChange(sw));
+    this.onStateChange(sw);  // Trigger updates for the current state.
+  }
+
+  async install() {
+    if (!('serviceWorker' in navigator)) return;  // Not supported.
+
+    if (await caches.has(BYPASS_ID)) {
+      this._bypassed = true;
+      console.log('Skipping service worker registration, disabled by the user');
+      return;
+    }
+    navigator.serviceWorker.register('service_worker.js').then(registration => {
+      this._initialWorker = registration.active;
+
+      // At this point there are two options:
+      // 1. This is the first time we visit the site (or cache was cleared) and
+      //    no SW is installed yet. In this case |installing| will be set.
+      // 2. A SW is already installed (though it might be obsolete). In this
+      //    case |active| will be set.
+      this.monitorWorker(registration.installing);
+      this.monitorWorker(registration.active);
+
+      // Setup the event that shows the "A new release is available"
+      // notification.
+      registration.addEventListener('updatefound', () => {
+        this.monitorWorker(registration.installing);
+      });
+    });
+  }
+
+  get bypassed() {
+     return this._bypassed;
+  }
+  get installing() {
+    return this._installing;
+  }
+}
\ No newline at end of file
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index 4e7c3aa..a295a75 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -311,12 +311,7 @@
   globals.frontendLocalState.localOnlyMode = false;
 
   if (e.target.dataset['useCatapultLegacyUi'] === '1') {
-    // Switch back to the old catapult UI.
-    if (isLegacyTrace(file.name)) {
-      openFileWithLegacyTraceViewer(file);
-      return;
-    }
-    openInOldUIWithSizeCheck(file);
+    openWithLegacyUi(file);
     return;
   }
 
@@ -343,7 +338,15 @@
   }
 
   globals.dispatch(Actions.openTraceFromFile({file}));
+}
 
+async function openWithLegacyUi(file: File) {
+  // Switch back to the old catapult UI.
+  if (await isLegacyTrace(file)) {
+    openFileWithLegacyTraceViewer(file);
+    return;
+  }
+  openInOldUIWithSizeCheck(file);
 }
 
 function openInOldUIWithSizeCheck(trace: Blob) {
@@ -464,7 +467,7 @@
 }
 
 
-const SidebarFooter: m.Component = {
+const EngineRPCWidget: m.Component = {
   view() {
     let cssClass = '';
     let title = 'Number of pending SQL queries';
@@ -478,7 +481,7 @@
     for (const engine of engines) {
       mode = engine.mode;
       if (engine.failed !== undefined) {
-        cssClass += '.failed';
+        cssClass += '.red';
         title = 'Query engine crashed\n' + engine.failed;
         failed = true;
       }
@@ -499,7 +502,7 @@
     }
 
     if (mode === 'HTTP_RPC') {
-      cssClass += '.rpc';
+      cssClass += '.green';
       label = 'RPC';
       title += '\n(Query engine: native accelerator over HTTP+RPC)';
     } else {
@@ -508,6 +511,88 @@
     }
 
     return m(
+        `.dbg-info-square${cssClass}`,
+        {title},
+        m('div', label),
+        m('div', `${failed ? 'FAIL' : globals.numQueuedQueries}`));
+  }
+};
+
+const ServiceWorkerWidget: m.Component = {
+  view() {
+    let cssClass = '';
+    let title = 'Service Worker: ';
+    let label = 'N/A';
+    const ctl = globals.serviceWorkerController;
+    if ((!('serviceWorker' in navigator))) {
+      label = 'N/A';
+      title += 'not supported by the browser (requires HTTPS)';
+    } else if (ctl.bypassed) {
+      label = 'OFF';
+      cssClass = '.red';
+      title += 'Bypassed, using live network. Double-click to re-enable';
+    } else if (ctl.installing) {
+      label = 'UPD';
+      cssClass = '.amber';
+      title += 'Installing / updating ...';
+    } else if (!navigator.serviceWorker.controller) {
+      label = 'N/A';
+      title += 'Not available, using network';
+    } else {
+      label = 'ON';
+      cssClass = '.green';
+      title += 'Serving from cache. Ready for offline use';
+    }
+
+    const toggle = async () => {
+      if (globals.serviceWorkerController.bypassed) {
+        globals.serviceWorkerController.setBypass(false);
+        return;
+      }
+      showModal({
+        title: 'Disable service worker?',
+        content: m(
+            'div',
+            m('p', `If you continue the service worker will be disabled until
+                      manually re-enabled.`),
+            m('p', `All future requests will be served from the network and the
+                    UI won't be available offline.`),
+            m('p', `You should do this only if you are debugging the UI
+                    or if you are experiencing caching-related problems.`),
+            m('p', `Disabling will cause a refresh of the UI, the current state
+                    will be lost.`),
+            ),
+        buttons: [
+          {
+            text: 'Disable and reload',
+            primary: true,
+            id: 'sw-bypass-enable',
+            action: () => {
+              globals.serviceWorkerController.setBypass(true).then(
+                  () => location.reload());
+            }
+          },
+          {
+            text: 'Cancel',
+            primary: false,
+            id: 'sw-bypass-cancel',
+            action: () => {}
+          }
+        ]
+      });
+    };
+
+    return m(
+        `.dbg-info-square${cssClass}`,
+        {title, ondblclick: toggle},
+        m('div', 'SW'),
+        m('div', label));
+  }
+};
+
+const SidebarFooter: m.Component = {
+  view() {
+    return m(
         '.sidebar-footer',
         m('button',
           {
@@ -516,10 +601,8 @@
           m('i.material-icons',
             {title: 'Toggle Perf Debug Mode'},
             'assessment')),
-        m(`.num-queued-queries${cssClass}`,
-          {title},
-          m('div', label),
-          m('div', `${failed ? 'FAIL' : globals.numQueuedQueries}`)),
+        m(EngineRPCWidget),
+        m(ServiceWorkerWidget),
     );
   }
 };
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 2163ace..a7bd25f 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -32,6 +32,8 @@
   [COMMAND]: 'e.g. select * from sched left join thread using(utid) limit 10'
 };
 
+export const DISMISSED_PANNING_HINT_KEY = 'dismissedPanningHint';
+
 let selResult = 0;
 let numResults = 0;
 let mode: Mode = SEARCH;
@@ -229,8 +231,65 @@
   }
 }
 
+
+class NewVersionNotification implements m.ClassComponent {
+  view() {
+    if (!globals.frontendLocalState.newVersionAvailable) return;
+    return m(
+        '.new-version-toast',
+        'A new version of the UI is available!',
+        m('button.notification-btn.preferred',
+          {
+            onclick: () => {
+              location.reload();
+            }
+          },
+          'Reload'),
+        m('button.notification-btn',
+          {
+            onclick: () => {
+              globals.frontendLocalState.newVersionAvailable = false;
+              globals.rafScheduler.scheduleFullRedraw();
+            }
+          },
+          'Dismiss'),
+    );
+  }
+}
+
+
+class HelpPanningNotification implements m.ClassComponent {
+  view() {
+    const dismissed = localStorage.getItem(DISMISSED_PANNING_HINT_KEY);
+    if (dismissed === 'true' || !globals.frontendLocalState.showPanningHint) {
+      return;
+    }
+    return m(
+        '.helpful-hint',
+        m('.hint-text',
+          'Are you trying to pan? Use the WASD keys or hold shift to click ' +
+              'and drag. Press \'?\' for more help.'),
+        m('button.hint-dismiss-button',
+          {
+            onclick: () => {
+              globals.frontendLocalState.showPanningHint = false;
+              localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
+              globals.rafScheduler.scheduleFullRedraw();
+            }
+          },
+          'Dismiss'),
+    );
+  }
+}
+
 export class Topbar implements m.ClassComponent {
   view() {
-    return m('.topbar', m(Omnibox), m(Progress));
+    return m(
+        '.topbar',
+        globals.frontendLocalState.newVersionAvailable ?
+            m(NewVersionNotification) :
+            m(Omnibox),
+        m(Progress),
+        m(HelpPanningNotification));
   }
 }
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index b8c778d..098bd81 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -198,7 +198,7 @@
         '.track',
         {
           style: {
-            height: `${attrs.track.getHeight()}px`,
+            height: `${Math.max(24, attrs.track.getHeight())}px`,
           },
           id: 'track_' + attrs.trackState.id,
         },
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 49e6d22..aa8d0c4 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -32,6 +32,7 @@
 import {TimeAxisPanel} from './time_axis_panel';
 import {computeZoom} from './time_scale';
 import {TimeSelectionPanel} from './time_selection_panel';
+import {DISMISSED_PANNING_HINT_KEY} from './topbar';
 import {TrackGroupPanel} from './track_group_panel';
 import {TrackPanel} from './track_panel';
 import {VideoPanel} from './video_panel';
@@ -131,7 +132,10 @@
     const frontendLocalState = globals.frontendLocalState;
     const updateDimensions = () => {
       const rect = vnode.dom.getBoundingClientRect();
-      frontendLocalState.updateResolution(0, rect.width - TRACK_SHELL_WIDTH);
+      frontendLocalState.updateResolution(
+          0,
+          rect.width - TRACK_SHELL_WIDTH -
+              frontendLocalState.getScrollbarWidth());
     };
 
     updateDimensions();
@@ -167,6 +171,8 @@
           tStart = tEnd - origDelta;
         }
         frontendLocalState.updateVisibleTime(new TimeSpan(tStart, tEnd));
+        // If the user has panned they no longer need the hint.
+        localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
         globals.rafScheduler.scheduleRedraw();
       },
       onZoomed: (zoomedPositionPx: number, zoomRatio: number) => {
@@ -179,10 +185,10 @@
         frontendLocalState.updateVisibleTime(newSpan);
         globals.rafScheduler.scheduleRedraw();
       },
-      shouldDrag: (currentPx: number) => {
+      editSelection: (currentPx: number) => {
         return onTimeRangeBoundary(currentPx) !== null;
       },
-      onDrag: (
+      onSelection: (
           dragStartX: number,
           dragStartY: number,
           prevX: number,
diff --git a/ui/src/service_worker/service_worker.ts b/ui/src/service_worker/service_worker.ts
new file mode 100644
index 0000000..3289352
--- /dev/null
+++ b/ui/src/service_worker/service_worker.ts
@@ -0,0 +1,166 @@
+// Copyright (C) 2020 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.
+
+// This script handles the caching of the UI resources, allowing it to work
+// offline (as long as the UI site has been visited at least once).
+// Design doc: http://go/perfetto-offline.
+
+// When a new version of the UI is released (e.g. v1 -> v2), the following
+// happens on the next visit:
+// 1. The v1 (old) service worker is activated (at this point we don't know yet
+//    that v2 is released).
+// 2. /index.html is requested. The SW intercepts the request and serves
+//    v1 from cache.
+// 3. The browser checks if a new version of service_worker.js is available. It
+//    does that by comparing the bytes of the current and new version.
+// 5. service_worker.js v2 will not be byte identical with v1, even if v2 was a
+//    css-only change. This is due to the hashes in UI_DIST_MAP below. For this
+//    reason v2 is installed in the background (it takes several seconds).
+// 6. The 'install' handler is triggered, the new resources are fetched and
+//    populated in the cache.
+// 7. The 'activate' handler is triggered. The old caches are deleted at this
+//    point.
+// 8. frontend/index.ts (in setupServiceWorker()) is notified about the activate
+//    and shows a notification prompting to reload the UI.
+//
+// If the user just closes the tab or hits refresh, v2 will be served anyways
+// on the next load.
+
+// UI_DIST_FILES is map of {file_name -> sha1}.
+// It is really important that this map is bundled directly in the
+// service_worker.js bundle file, as it's used to cause the browser to
+// re-install the service worker and re-fetch resources when anything changes.
+// This is why the map contains the SHA1s even if we don't directly use them in
+// the code (because it makes the final .js file content-dependent).
+
+import {UI_DIST_MAP} from '../gen/dist_file_map';
+
+declare var self: ServiceWorkerGlobalScope;
+
+const CACHE_NAME = 'dist-' + UI_DIST_MAP.hex_digest.substr(0, 16);
+const LOG_TAG = `ServiceWorker[${UI_DIST_MAP.hex_digest.substr(0, 16)}]: `;
+
+
+function shouldHandleHttpRequest(req: Request): boolean {
+  // Suppress warning: 'only-if-cached' can be set only with 'same-origin' mode.
+  // This seems to be a chromium bug. An internal code search suggests this is a
+  // socially acceptable workaround.
+  if (req.cache === 'only-if-cached' && req.mode !== 'same-origin') {
+    return false;
+  }
+
+  const url = new URL(req.url);
+  return req.method === 'GET' && url.origin === self.location.origin;
+}
+
+async function handleHttpRequest(req: Request): Promise<Response> {
+  if (!shouldHandleHttpRequest(req)) {
+    throw new Error(LOG_TAG + `${req.url} shouldn't have been handled`);
+  }
+
+  // We serve from the cache even if req.cache == 'no-cache'. It's a bit
+  // contra-intuitive but it's the most consistent option. If the user hits the
+  // reload button*, the browser requests the "/" index with a 'no-cache' fetch.
+  // However all the other resources (css, js, ...) are requested with a
+  // 'default' fetch (this is just how Chrome works, it's not us). If we bypass
+  // the service worker cache when we get a 'no-cache' request, we can end up in
+  // an inconsistent state where the index.html is more recent than the other
+  // resources, which is undesirable.
+  // * Only Ctrl+R. Ctrl+Shift+R will always bypass service-worker for all the
+  // requests (index.html and the rest) made in that tab.
+  try {
+    const cacheOps = {cacheName: CACHE_NAME} as CacheQueryOptions;
+    const cachedRes = await caches.match(req, cacheOps);
+    if (cachedRes) {
+      console.debug(LOG_TAG + `serving ${req.url} from cache`);
+      return cachedRes;
+    }
+    console.warn(LOG_TAG + `cache miss on ${req.url}`);
+  } catch (exc) {
+    console.error(LOG_TAG + `Cache request failed for ${req.url}`, exc);
+  }
+
+  // In any other case, just propagate the fetch on the network, which is the
+  // safe behavior.
+  console.debug(LOG_TAG + `falling back on network fetch() for ${req.url}`);
+  return fetch(req);
+}
+
+// The install() event is fired:
+// - The very first time the site is visited, after frontend/index.ts has
+//   executed the serviceWorker.register() method.
+// - *After* the site is loaded, if the service_worker.js code
+//   has changed (because of the hashes in UI_DIST_MAP, service_worker.js will
+//   change if anything in the UI has changed).
+self.addEventListener('install', event => {
+  const doInstall = async () => {
+    if (await caches.has('BYPASS_SERVICE_WORKER')) {
+      // Throw will prevent the installation.
+      throw new Error(LOG_TAG + 'skipping installation, bypass enabled');
+    }
+    console.log(LOG_TAG + 'installation started');
+    const cache = await caches.open(CACHE_NAME);
+    const urlsToCache: RequestInfo[] = [];
+    for (const [file, integrity] of Object.entries(UI_DIST_MAP.files)) {
+      const reqOpts:
+          RequestInit = {cache: 'reload', mode: 'same-origin', integrity};
+      urlsToCache.push(new Request(file, reqOpts));
+      if (file === 'index.html' && location.host !== 'storage.googleapis.com') {
+        // Disable cachinig of '/' for cases where the UI is hosted on GCS.
+        // GCS doesn't support auto indexes. GCS returns a 404 page on / that
+        // fails the integrity check.
+        urlsToCache.push(new Request('/', reqOpts));
+      }
+    }
+    await cache.addAll(urlsToCache);
+    console.log(LOG_TAG + 'installation completed');
+
+    // skipWaiting() still waits for the install to be complete. Without this
+    // call, the new version would be activated only when all tabs are closed.
+    // Instead, we ask to activate it immediately. This is safe because each
+    // service worker version uses a different cache named after the SHA256 of
+    // the contents. When the old version is activated, the activate() method
+    // below will evict the cache for the old versions. If there is an old still
+    // opened, any further request from that tab will be a cache-miss and go
+    // through the network (which is inconsitent, but not the end of the world).
+    self.skipWaiting();
+  };
+  event.waitUntil(doInstall());
+});
+
+self.addEventListener('activate', (event) => {
+  console.warn(LOG_TAG + 'activated');
+  const doActivate = async () => {
+    // Clear old caches.
+    for (const key of await caches.keys()) {
+      if (key !== CACHE_NAME) await caches.delete(key);
+    }
+    // This makes a difference only for the very first load, when no service
+    // worker is present. In all the other cases the skipWaiting() will hot-swap
+    // the active service worker anyways.
+    await self.clients.claim();
+  };
+  event.waitUntil(doActivate());
+});
+
+self.addEventListener('fetch', event => {
+  // The early return here will cause the browser to fall back on standard
+  // network-based fetch.
+  if (!shouldHandleHttpRequest(event.request)) {
+    console.debug(LOG_TAG + `serving ${event.request.url} from network`);
+    return;
+  }
+
+  event.respondWith(handleHttpRequest(event.request));
+});
diff --git a/ui/src/service_worker/tsconfig.json b/ui/src/service_worker/tsconfig.json
new file mode 100644
index 0000000..7b58182
--- /dev/null
+++ b/ui/src/service_worker/tsconfig.json
@@ -0,0 +1,14 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "include": [ "." ],
+  "exclude": [
+    "../gen/"
+  ],
+  "compilerOptions": {
+    "lib": [
+      "webworker",
+      "es2018",
+    ],
+    "types" : []
+  }
+}
diff --git a/ui/src/tracks/chrome_slices/common.ts b/ui/src/tracks/chrome_slices/common.ts
index faa40c3..41eb3c2 100644
--- a/ui/src/tracks/chrome_slices/common.ts
+++ b/ui/src/tracks/chrome_slices/common.ts
@@ -18,8 +18,6 @@
 
 export interface Config {
   maxDepth: number;
-  upid: number;
-  utid: number;
   trackId: number;
 }
 
diff --git a/ui/src/tracks/heap_profile/common.ts b/ui/src/tracks/heap_profile/common.ts
index fc9da62..0a8a16d 100644
--- a/ui/src/tracks/heap_profile/common.ts
+++ b/ui/src/tracks/heap_profile/common.ts
@@ -17,6 +17,7 @@
 
 export interface Data extends TrackData {
   tsStarts: Float64Array;
+  types: string[];
 }
 
 export interface Config {
diff --git a/ui/src/tracks/heap_profile/controller.ts b/ui/src/tracks/heap_profile/controller.ts
index 61ad335..bab0b15 100644
--- a/ui/src/tracks/heap_profile/controller.ts
+++ b/ui/src/tracks/heap_profile/controller.ts
@@ -28,11 +28,23 @@
   async onBoundsChange(start: number, end: number, resolution: number):
       Promise<Data> {
     if (this.config.upid === undefined) {
-      return {start, end, resolution, length: 0, tsStarts: new Float64Array()};
+      return {
+        start,
+        end,
+        resolution,
+        length: 0,
+        tsStarts: new Float64Array(),
+        types: new Array<string>()
+      };
     }
     const result = await this.query(`
-    select distinct(ts) from heap_profile_allocation where upid = ${
-        this.config.upid}`);
+    select * from
+    (select distinct(ts) as ts, 'native' as type from heap_profile_allocation
+     where upid = ${this.config.upid}
+        union
+        select distinct(graph_sample_ts) as ts, 'graph' as type from
+        heap_graph_object
+        where upid = ${this.config.upid}) order by ts`);
     const numRows = +result.numRecords;
     const data: Data = {
       start,
@@ -40,10 +52,12 @@
       resolution,
       length: numRows,
       tsStarts: new Float64Array(numRows),
+      types: new Array<string>(numRows),
     };
 
     for (let row = 0; row < numRows; row++) {
       data.tsStarts[row] = +result.columns[0].longValues![row];
+      data.types[row] = result.columns[1].stringValues![row];
     }
 
     return data;
diff --git a/ui/src/tracks/heap_profile/frontend.ts b/ui/src/tracks/heap_profile/frontend.ts
index 8eaba5e..86d177e 100644
--- a/ui/src/tracks/heap_profile/frontend.ts
+++ b/ui/src/tracks/heap_profile/frontend.ts
@@ -118,10 +118,11 @@
 
     if (index !== -1) {
       const ts = data.tsStarts[index];
+      const type = data.types[index];
       globals.dispatch(Actions.showHeapProfileFlamegraph(
-          {id: index, upid: this.config.upid, ts}));
-      globals.makeSelection(
-          Actions.selectHeapProfile({id: index, upid: this.config.upid, ts}));
+          {id: index, upid: this.config.upid, ts, type}));
+      globals.makeSelection(Actions.selectHeapProfile(
+          {id: index, upid: this.config.upid, ts, type}));
       return true;
     }
     return false;
diff --git a/ui/src/tracks/thread_state/frontend.ts b/ui/src/tracks/thread_state/frontend.ts
index 0b01644..cae38f2 100644
--- a/ui/src/tracks/thread_state/frontend.ts
+++ b/ui/src/tracks/thread_state/frontend.ts
@@ -31,7 +31,7 @@
 } from './common';
 
 const MARGIN_TOP = 4;
-const RECT_HEIGHT = 12;
+const RECT_HEIGHT = 14;
 
 class ThreadStateTrack extends Track<Config, Data> {
   static readonly kind = THREAD_STATE_TRACK_KIND;
@@ -54,6 +54,11 @@
 
     if (data === undefined) return;  // Can't possibly draw anything.
 
+    const shouldGroupBusyStates = groupBusyStates(data.resolution);
+
+    ctx.textAlign = 'center';
+    ctx.font = '10px Roboto Condensed';
+
     for (let i = 0; i < data.starts.length; i++) {
       const tStart = data.starts[i];
       const tEnd = data.ends[i];
@@ -69,18 +74,16 @@
         const color = colorForState(state);
         ctx.fillStyle = `hsl(${color.h},${color.s}%,${color.l}%)`;
         let rectWidth = rectEnd - rectStart;
-        if (groupBusyStates(data.resolution) && rectWidth < 1) {
+        if (shouldGroupBusyStates && rectWidth < 1) {
           rectWidth = 1;
         }
         ctx.fillRect(rectStart, MARGIN_TOP, rectWidth, RECT_HEIGHT);
 
-        // Don't render text when we have less than 5px to play with.
-        if (rectWidth < 5) continue;
-        ctx.textAlign = 'center';
+        // Don't render text when we have less than 10px to play with.
+        if (rectWidth < 10) continue;
         const title = cropText(translateState(state), charWidth, rectWidth);
         const rectXCenter = rectStart + rectWidth / 2;
         ctx.fillStyle = color.l < 80 ? '#fff' : '#404040';
-        ctx.font = '10px Roboto Condensed';
         ctx.fillText(title, rectXCenter, MARGIN_TOP + RECT_HEIGHT / 2 + 3);
       }
     }
diff --git a/ui/tsconfig.base.json b/ui/tsconfig.base.json
new file mode 100644
index 0000000..e240301
--- /dev/null
+++ b/ui/tsconfig.base.json
@@ -0,0 +1,27 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "target": "es6",
+    "module": "commonjs",
+    "moduleResolution": "node",
+    // Lints and checks.
+    "allowJs": true,
+    "declaration": false,                  // Generates corresponding '.d.ts' file.
+    "sourceMap": true,                     // Generates corresponding '.map' file.
+    "outDir": "./dist",                    // Redirect output structure to the directory.
+    "removeComments": false,               // Do not emit comments to output.
+    "importHelpers": true,                 // Import emit helpers from 'tslib'.
+    "downlevelIteration": true,            // Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'.
+    "strict": true,                        // Enable all strict type-checking options.
+    "noImplicitAny": true,                 // Raise error on expressions and declarations with an implied 'any' type.
+    "strictNullChecks": true,              // Enable strict null checks.
+    "strictFunctionTypes": true,           // Enable strict checking of function types.
+    "strictPropertyInitialization": true,  // Enable strict checking of property initialization in classes.
+    "noImplicitThis": true,                // Raise error on 'this' expressions with an implied 'any' type.
+    "alwaysStrict": true,                  // Parse in strict mode and emit "use strict" for each source file.
+    "noUnusedLocals": true,                // Report errors on unused locals.
+    "noUnusedParameters": true,            // Report errors on unused parameters.
+    "noImplicitReturns": true,             // Report error when not all code paths in function return a value.
+    "noFallthroughCasesInSwitch": true,    // Report errors for fallthrough cases in switch statement.
+  }
+}
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
index e0564a8..8f2c266 100644
--- a/ui/tsconfig.json
+++ b/ui/tsconfig.json
@@ -1,7 +1,9 @@
 {
+  "extends": "./tsconfig.base.json",
   "include": [ "src/" ],
   "exclude": [
     "./node_modules/",
+    "./src/service_worker/",
     "./src/gen/"
   ],
   "compilerOptions": {
@@ -9,31 +11,8 @@
       "dom",                               // Need to be explicitly mentioned now since we're overriding default included libs.
       "es2018",                            // Need this to use Object.values.
     ],
-    "baseUrl": ".",
     "paths": {
       "*" : ["*", "./node_modules/@tsundoku/micromodal_types/*"]
     },
-    "target": "es6",
-    "module": "commonjs",
-    "moduleResolution": "node",
-    // Lints and checks.
-    "allowJs": true,
-    "declaration": false,                  // Generates corresponding '.d.ts' file.
-    "sourceMap": true,                     // Generates corresponding '.map' file.
-    "outDir": "./dist",                    // Redirect output structure to the directory.
-    "removeComments": false,               // Do not emit comments to output.
-    "importHelpers": true,                 // Import emit helpers from 'tslib'.
-    "downlevelIteration": true,            // Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'.
-    "strict": true,                        // Enable all strict type-checking options.
-    "noImplicitAny": true,                 // Raise error on expressions and declarations with an implied 'any' type.
-    "strictNullChecks": true,              // Enable strict null checks.
-    "strictFunctionTypes": true,           // Enable strict checking of function types.
-    "strictPropertyInitialization": true,  // Enable strict checking of property initialization in classes.
-    "noImplicitThis": true,                // Raise error on 'this' expressions with an implied 'any' type.
-    "alwaysStrict": true,                  // Parse in strict mode and emit "use strict" for each source file.
-    "noUnusedLocals": true,                // Report errors on unused locals.
-    "noUnusedParameters": true,            // Report errors on unused parameters.
-    "noImplicitReturns": true,             // Report error when not all code paths in function return a value.
-    "noFallthroughCasesInSwitch": true,    // Report errors for fallthrough cases in switch statement.
   }
 }